Merge branch 'version-13-beta-pre-release' of https://github.com/frappe/erpnext into add_account_method_v13
diff --git a/.github/helper/documentation.py b/.github/helper/documentation.py
index b603ed5..9cc4663 100644
--- a/.github/helper/documentation.py
+++ b/.github/helper/documentation.py
@@ -21,8 +21,8 @@
 			if word.startswith('http') and uri_validator(word):
 				parsed_url = urlparse(word)
 				if parsed_url.netloc == "github.com":
-					_, org, repo, _type, ref = parsed_url.path.split('/')
-					if org == "frappe" and repo in docs_repos:
+					parts = parsed_url.path.split('/')
+					if len(parts) == 5 and parts[1] == "frappe" and parts[2] in docs_repos:
 						return True
 
 
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index c108333..b122e5f 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -5,7 +5,7 @@
 from erpnext.hooks import regional_overrides
 from frappe.utils import getdate
 
-__version__ = '13.0.0-beta.7'
+__version__ = '13.0.0-beta.13'
 
 def get_default_company(user=None):
 	'''Get default company for user'''
@@ -132,16 +132,10 @@
 
 	return caller
 
-def get_last_membership():
+def get_last_membership(member):
 	'''Returns last membership if exists'''
 	last_membership = frappe.get_all('Membership', 'name,to_date,membership_type',
-		dict(member=frappe.session.user, paid=1), order_by='to_date desc', limit=1)
+		dict(member=member, paid=1), order_by='to_date desc', limit=1)
 
-	return last_membership and last_membership[0]
-
-def is_member():
-	'''Returns true if the user is still a member'''
-	last_membership = get_last_membership()
-	if last_membership and getdate(last_membership.to_date) > getdate():
-		return True
-	return False
+	if last_membership:
+		return last_membership[0]
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json
index 3fc109b..849df18 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json
@@ -910,98 +910,8 @@
             },
             "is_group": 1
         },
-        "Passiva": {
+        "Passiva - Verbindlichkeiten": {
             "root_type": "Liability",
-            "A - Eigenkapital": {
-                "account_type": "Equity",
-                "is_group": 1,
-                "I - Gezeichnetes Kapital": {
-                    "account_type": "Equity",
-					"is_group": 1,
-					"Gezeichnetes Kapital": {
-						"account_type": "Equity",
-						"account_number": "2900"
-					},
-					"Ausstehende Einlagen auf das gezeichnete Kapital": {
-						"account_number": "2910",
-						"is_group": 1
-					}
-                },
-                "II - Kapitalr\u00fccklage": {
-                    "account_type": "Equity",
-					"is_group": 1,
-					"Kapitalr\u00fccklage": {
-						"account_number": "2920"
-					}
-                },
-                "III - Gewinnr\u00fccklagen": {
-                    "account_type": "Equity",
-                    "1 - gesetzliche R\u00fccklage": {
-                        "account_type": "Equity",
-						"is_group": 1,
-						"Gesetzliche R\u00fccklage": {
-							"account_number": "2930"
-						}
-                    },
-                    "2 - R\u00fccklage f. Anteile an einem herrschenden oder mehrheitlich beteiligten Unternehmen": {
-                        "account_type": "Equity",
-                        "is_group": 1
-                    },
-                    "3 - satzungsm\u00e4\u00dfige R\u00fccklagen": {
-                        "account_type": "Equity",
-						"is_group": 1,
-						"Satzungsm\u00e4\u00dfige R\u00fccklagen": {
-							"account_number": "2950"
-						}
-                    },
-                    "4 - andere Gewinnr\u00fccklagen": {
-                        "account_type": "Equity",
-                        "is_group": 1,
-                        "Gewinnr\u00fccklagen aus den \u00dcbergangsvorschriften BilMoG": {
-                            "is_group": 1,
-                            "Gewinnr\u00fccklagen (BilMoG)": {
-                                "account_number": "2963"
-                            },
-                            "Gewinnr\u00fccklagen aus Zuschreibung Sachanlageverm\u00f6gen (BilMoG)": {
-                                "account_number": "2964"
-                            },
-                            "Gewinnr\u00fccklagen aus Zuschreibung Finanzanlageverm\u00f6gen (BilMoG)": {
-                                "account_number": "2965"
-                            },
-                            "Gewinnr\u00fccklagen aus Aufl\u00f6sung der Sonderposten mit R\u00fccklageanteil (BilMoG)": {
-                                "account_number": "2966"
-                            }
-                        },
-                        "Latente Steuern (Gewinnr\u00fccklage Haben) aus erfolgsneutralen Verrechnungen": {
-                            "account_number": "2967"
-                        },
-                        "Latente Steuern (Gewinnr\u00fccklage Soll) aus erfolgsneutralen Verrechnungen": {
-                            "account_number": "2968"
-                        },
-                        "Rechnungsabgrenzungsposten (Gewinnr\u00fccklage Soll) aus erfolgsneutralen Verrechnungen": {
-                            "account_number": "2969"
-                        }
-                    },
-                    "is_group": 1
-                },
-                "IV - Gewinnvortrag/Verlustvortrag": {
-                    "account_type": "Equity",
-                    "is_group": 1,
-					"Gewinnvortrag vor Verwendung": {
-						"account_number": "2970"
-					},
-					"Verlustvortrag vor Verwendung": {
-						"account_number": "2978"
-					}
-                },
-                "V - Jahres\u00fcberschu\u00df/Jahresfehlbetrag": {
-                    "account_type": "Equity",
-                    "is_group": 1
-                },
-                "Einlagen stiller Gesellschafter": {
-                    "account_number": "9295"
-                }
-            },
             "B - R\u00fcckstellungen": {
                 "is_group": 1,
                 "1 - R\u00fcckstellungen f. Pensionen und \u00e4hnliche Verplicht.": {
@@ -1618,6 +1528,143 @@
             },
             "is_group": 1
         },
+        "Passiva - Eigenkapital": {
+            "root_type": "Equity",
+            "A - Eigenkapital": {
+                "account_type": "Equity",
+                "is_group": 1,
+                "I - Gezeichnetes Kapital": {
+                    "account_type": "Equity",
+                    "is_group": 1,
+                    "Gezeichnetes Kapital": {
+                        "account_number": "2900",
+                        "account_type": "Equity"
+                    },
+                    "Gesch\u00e4ftsguthaben der verbleibenden Mitglieder": {
+                        "account_number": "2901"
+                    },
+                    "Gesch\u00e4ftsguthaben der ausscheidenden Mitglieder": {
+                        "account_number": "2902"
+                    },
+                    "Gesch\u00e4ftsguthaben aus gek\u00fcndigten Gesch\u00e4ftsanteilen": {
+                        "account_number": "2903"
+                    },
+                    "R\u00fcckst\u00e4ndige f\u00e4llige Einzahlungen auf Gesch\u00e4ftsanteile, vermerkt": {
+                        "account_number": "2906"
+                    },
+                    "Gegenkonto R\u00fcckst\u00e4ndige f\u00e4llige Einzahlungen auf Gesch\u00e4ftsanteile, vermerkt": {
+                        "account_number": "2907"
+                    },
+                    "Kapitalerh\u00f6hung aus Gesellschaftsmitteln": {
+                        "account_number": "2908"
+                    },
+                    "Ausstehende Einlagen auf das gezeichnete Kapital, nicht eingefordert": {
+                        "account_number": "2910"
+                    }
+                },
+                "II - Kapitalr\u00fccklage": {
+                    "account_type": "Equity",
+                    "is_group": 1,
+                    "Kapitalr\u00fccklage": {
+                        "account_number": "2920"
+                    },
+                    "Kapitalr\u00fccklage durch Ausgabe von Anteilen \u00fcber Nennbetrag": {
+                        "account_number": "2925"
+                    },
+                    "Kapitalr\u00fccklage durch Ausgabe von Schuldverschreibungen": {
+                        "account_number": "2926"
+                    },
+                    "Kapitalr\u00fccklage durch Zuzahlungen gegen Gew\u00e4hrung eines Vorzugs": {
+                        "account_number": "2927"
+                    },
+                    "Kapitalr\u00fccklage durch Zuzahlungen in das Eigenkapital": {
+                        "account_number": "2928"
+                    },
+                    "Nachschusskapital (Gegenkonto 1299)": {
+                        "account_number": "2929"
+                    }
+                },
+                "III - Gewinnr\u00fccklagen": {
+                    "account_type": "Equity",
+                    "1 - gesetzliche R\u00fccklage": {
+                        "account_type": "Equity",
+                        "is_group": 1,
+                        "Gesetzliche R\u00fccklage": {
+                            "account_number": "2930"
+                        }
+                    },
+                    "2 - R\u00fccklage f. Anteile an einem herrschenden oder mehrheitlich beteiligten Unternehmen": {
+                        "account_type": "Equity",
+                        "is_group": 1,
+                        "R\u00fccklage f. Anteile an einem herrschenden oder mehrheitlich beteiligten Unternehmen": {
+                            "account_number": "2935"
+                        }
+                    },
+                    "3 - satzungsm\u00e4\u00dfige R\u00fccklagen": {
+                        "account_type": "Equity",
+                        "is_group": 1,
+                        "Satzungsm\u00e4\u00dfige R\u00fccklagen": {
+                            "account_number": "2950"
+                        }
+                    },
+                    "4 - andere Gewinnr\u00fccklagen": {
+                        "account_type": "Equity",
+                        "is_group": 1,
+                        "Andere Gewinnr\u00fccklagen": {
+                            "account_number": "2960"
+                        },
+                        "Andere Gewinnr\u00fccklagen aus dem Erwerb eigener Anteile": {
+                            "account_number": "2961"
+                        },
+                        "Eigenkapitalanteil von Wertaufholungen": {
+                            "account_number": "2962"
+                        },
+                        "Gewinnr\u00fccklagen aus den \u00dcbergangsvorschriften BilMoG": {
+                            "is_group": 1,
+                            "Gewinnr\u00fccklagen (BilMoG)": {
+                                "account_number": "2963"
+                            },
+                            "Gewinnr\u00fccklagen aus Zuschreibung Sachanlageverm\u00f6gen (BilMoG)": {
+                                "account_number": "2964"
+                            },
+                            "Gewinnr\u00fccklagen aus Zuschreibung Finanzanlageverm\u00f6gen (BilMoG)": {
+                                "account_number": "2965"
+                            },
+                            "Gewinnr\u00fccklagen aus Aufl\u00f6sung der Sonderposten mit R\u00fccklageanteil (BilMoG)": {
+                                "account_number": "2966"
+                            }
+                        },
+                        "Latente Steuern (Gewinnr\u00fccklage Haben) aus erfolgsneutralen Verrechnungen": {
+                            "account_number": "2967"
+                        },
+                        "Latente Steuern (Gewinnr\u00fccklage Soll) aus erfolgsneutralen Verrechnungen": {
+                            "account_number": "2968"
+                        },
+                        "Rechnungsabgrenzungsposten (Gewinnr\u00fccklage Soll) aus erfolgsneutralen Verrechnungen": {
+                            "account_number": "2969"
+                        }
+                    },
+                    "is_group": 1
+                },
+                "IV - Gewinnvortrag/Verlustvortrag": {
+                    "account_type": "Equity",
+                    "is_group": 1,
+                    "Gewinnvortrag vor Verwendung": {
+                        "account_number": "2970"
+                    },
+                    "Verlustvortrag vor Verwendung": {
+                        "account_number": "2978"
+                    }
+                },
+                "V - Jahres\u00fcberschu\u00df/Jahresfehlbetrag": {
+                    "account_type": "Equity",
+                    "is_group": 1
+                },
+                "Einlagen stiller Gesellschafter": {
+                    "account_number": "9295"
+                }
+            }
+        },
         "1 - Umsatzerl\u00f6se": {
             "root_type": "Income",
             "is_group": 1,
diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py
index 113bea0..533eda3 100644
--- a/erpnext/accounts/doctype/account/test_account.py
+++ b/erpnext/accounts/doctype/account/test_account.py
@@ -254,7 +254,8 @@
 			account_name = kwargs.get('account_name'),
 			account_type = kwargs.get('account_type'),
 			parent_account = kwargs.get('parent_account'),
-			company = kwargs.get('company')
+			company = kwargs.get('company'),
+			account_currency = kwargs.get('account_currency')
 		))
 
 		account.save()
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
index 9a6c389..65c5ff1 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
@@ -2,7 +2,6 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on('Accounting Dimension', {
-
 	refresh: function(frm) {
 		frm.set_query('document_type', () => {
 			let invalid_doctypes = frappe.model.core_doctypes_list;
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index f888d9e..239588f 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -33,11 +33,11 @@
 		if frappe.flags.in_test:
 			make_dimension_in_accounting_doctypes(doc=self)
 		else:
-			frappe.enqueue(make_dimension_in_accounting_doctypes, doc=self)
+			frappe.enqueue(make_dimension_in_accounting_doctypes, doc=self, queue='long')
 
 	def on_trash(self):
 		if frappe.flags.in_test:
-			delete_accounting_dimension(doc=self)
+			delete_accounting_dimension(doc=self, queue='long')
 		else:
 			frappe.enqueue(delete_accounting_dimension, doc=self)
 
@@ -48,6 +48,9 @@
 		if not self.fieldname:
 			self.fieldname = scrub(self.label)
 
+	def on_update(self):
+		frappe.flags.accounting_dimensions = None
+
 def make_dimension_in_accounting_doctypes(doc):
 	doclist = get_doctypes_with_dimensions()
 	doc_count = len(get_accounting_dimensions())
@@ -165,9 +168,9 @@
 		frappe.clear_cache(doctype=doctype)
 
 def get_doctypes_with_dimensions():
-	doclist = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset",
+	doclist = ["GL Entry", "Sales Invoice", "POS Invoice", "Purchase Invoice", "Payment Entry", "Asset",
 		"Expense Claim", "Expense Claim Detail", "Expense Taxes and Charges", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note",
-		"Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
+		"Sales Invoice Item", "POS Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
 		"Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
 		"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
 		"Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription",
@@ -176,12 +179,14 @@
 	return doclist
 
 def get_accounting_dimensions(as_list=True):
-	accounting_dimensions = frappe.get_all("Accounting Dimension", fields=["label", "fieldname", "disabled", "document_type"])
+	if frappe.flags.accounting_dimensions is None:
+		frappe.flags.accounting_dimensions = frappe.get_all("Accounting Dimension",
+			fields=["label", "fieldname", "disabled", "document_type"])
 
 	if as_list:
-		return [d.fieldname for d in accounting_dimensions]
+		return [d.fieldname for d in frappe.flags.accounting_dimensions]
 	else:
-		return accounting_dimensions
+		return frappe.flags.accounting_dimensions
 
 def get_checks_for_pl_and_bs_accounts():
 	dimensions = frappe.db.sql("""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
@@ -203,7 +208,7 @@
 	return all_dimensions
 
 @frappe.whitelist()
-def get_dimension_filters():
+def get_dimensions(with_cost_center_and_project=False):
 	dimension_filters = frappe.db.sql("""
 		SELECT label, fieldname, document_type
 		FROM `tabAccounting Dimension`
@@ -214,6 +219,18 @@
 		FROM `tabAccounting Dimension Detail` c, `tabAccounting Dimension` p
 		WHERE c.parent = p.name""", as_dict=1)
 
+	if with_cost_center_and_project:
+		dimension_filters.extend([
+			{
+				'fieldname': 'cost_center',
+				'document_type': 'Cost Center'
+			},
+			{
+				'fieldname': 'project',
+				'document_type': 'Project'
+			}
+		])
+
 	default_dimensions_map = {}
 	for dimension in default_dimensions:
 		default_dimensions_map.setdefault(dimension.company, {})
diff --git a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
index 104880f..fc1d7e3 100644
--- a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
@@ -11,37 +11,7 @@
 
 class TestAccountingDimension(unittest.TestCase):
 	def setUp(self):
-		frappe.set_user("Administrator")
-
-		if not frappe.db.exists("Accounting Dimension", {"document_type": "Department"}):
-			dimension = frappe.get_doc({
-				"doctype": "Accounting Dimension",
-				"document_type": "Department",
-			}).insert()
-		else:
-			dimension1 = frappe.get_doc("Accounting Dimension", "Department")
-			dimension1.disabled = 0
-			dimension1.save()
-
-		if not frappe.db.exists("Accounting Dimension", {"document_type": "Location"}):
-			dimension1 = frappe.get_doc({
-				"doctype": "Accounting Dimension",
-				"document_type": "Location",
-			})
-
-			dimension1.append("dimension_defaults", {
-				"company": "_Test Company",
-				"reference_document": "Location",
-				"default_dimension": "Block 1",
-				"mandatory_for_bs": 1
-			})
-
-			dimension1.insert()
-			dimension1.save()
-		else:
-			dimension1 = frappe.get_doc("Accounting Dimension", "Location")
-			dimension1.disabled = 0
-			dimension1.save()
+		create_dimension()
 
 	def test_dimension_against_sales_invoice(self):
 		si = create_sales_invoice(do_not_save=1)
@@ -101,6 +71,38 @@
 	def tearDown(self):
 		disable_dimension()
 
+def create_dimension():
+	frappe.set_user("Administrator")
+
+	if not frappe.db.exists("Accounting Dimension", {"document_type": "Department"}):
+		frappe.get_doc({
+			"doctype": "Accounting Dimension",
+			"document_type": "Department",
+		}).insert()
+	else:
+		dimension = frappe.get_doc("Accounting Dimension", "Department")
+		dimension.disabled = 0
+		dimension.save()
+
+	if not frappe.db.exists("Accounting Dimension", {"document_type": "Location"}):
+		dimension1 = frappe.get_doc({
+			"doctype": "Accounting Dimension",
+			"document_type": "Location",
+		})
+
+		dimension1.append("dimension_defaults", {
+			"company": "_Test Company",
+			"reference_document": "Location",
+			"default_dimension": "Block 1",
+			"mandatory_for_bs": 1
+		})
+
+		dimension1.insert()
+		dimension1.save()
+	else:
+		dimension1 = frappe.get_doc("Accounting Dimension", "Location")
+		dimension1.disabled = 0
+		dimension1.save()
 
 def disable_dimension():
 	dimension1 = frappe.get_doc("Accounting Dimension", "Department")
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/accounts/doctype/accounting_dimension_filter/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/accounts/doctype/accounting_dimension_filter/__init__.py
diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js
new file mode 100644
index 0000000..74b7b51
--- /dev/null
+++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js
@@ -0,0 +1,82 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Accounting Dimension Filter', {
+	refresh: function(frm, cdt, cdn) {
+		if (frm.doc.accounting_dimension) {
+			frm.set_df_property('dimensions', 'label', frm.doc.accounting_dimension, cdn, 'dimension_value');
+		}
+
+		let help_content =
+			`<table class="table table-bordered" style="background-color: #f9f9f9;">
+				<tr><td>
+					<p>
+						<i class="fa fa-hand-right"></i>
+						{{__('Note: On checking Is Mandatory the accounting dimension will become mandatory against that specific account for all accounting transactions')}}
+					</p>
+				</td></tr>
+			</table>`;
+
+		frm.set_df_property('dimension_filter_help', 'options', help_content);
+	},
+	onload: function(frm) {
+		frm.set_query('applicable_on_account', 'accounts', function() {
+			return {
+				filters: {
+					'company': frm.doc.company
+				}
+			};
+		});
+
+		frappe.db.get_list('Accounting Dimension',
+			{fields: ['document_type']}).then((res) => {
+			let options = ['Cost Center', 'Project'];
+
+			res.forEach((dimension) => {
+				options.push(dimension.document_type);
+			});
+
+			frm.set_df_property('accounting_dimension', 'options', options);
+		});
+
+		frm.trigger('setup_filters');
+	},
+
+	setup_filters: function(frm) {
+		let filters = {};
+
+		if (frm.doc.accounting_dimension) {
+			frappe.model.with_doctype(frm.doc.accounting_dimension, function() {
+				if (frappe.model.is_tree(frm.doc.accounting_dimension)) {
+					filters['is_group'] = 0;
+				}
+
+				if (frappe.meta.has_field(frm.doc.accounting_dimension, 'company')) {
+					filters['company'] = frm.doc.company;
+				}
+
+				frm.set_query('dimension_value', 'dimensions', function() {
+					return {
+						filters: filters
+					};
+				});
+			});
+		}
+	},
+
+	accounting_dimension: function(frm) {
+		frm.clear_table("dimensions");
+		let row = frm.add_child("dimensions");
+		row.accounting_dimension = frm.doc.accounting_dimension;
+		frm.refresh_field("dimensions");
+		frm.trigger('setup_filters');
+	},
+});
+
+frappe.ui.form.on('Allowed Dimension', {
+	dimensions_add: function(frm, cdt, cdn) {
+		let row = locals[cdt][cdn];
+		row.accounting_dimension = frm.doc.accounting_dimension;
+		frm.refresh_field("dimensions");
+	}
+});
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json
new file mode 100644
index 0000000..0f3fbc0
--- /dev/null
+++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.json
@@ -0,0 +1,158 @@
+{
+ "actions": [],
+ "autoname": "format:{accounting_dimension}-{#####}",
+ "creation": "2020-11-08 18:28:11.906146",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "accounting_dimension",
+  "disabled",
+  "column_break_2",
+  "company",
+  "allow_or_restrict",
+  "section_break_4",
+  "accounts",
+  "column_break_6",
+  "dimensions",
+  "section_break_10",
+  "dimension_filter_help"
+ ],
+ "fields": [
+  {
+   "fieldname": "accounting_dimension",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Accounting Dimension",
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "section_break_4",
+   "fieldtype": "Section Break",
+   "hide_border": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "column_break_6",
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "allow_or_restrict",
+   "fieldtype": "Select",
+   "label": "Allow Or Restrict Dimension",
+   "options": "Allow\nRestrict",
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "accounts",
+   "fieldtype": "Table",
+   "label": "Applicable On Account",
+   "options": "Applicable On Account",
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "depends_on": "eval:doc.accounting_dimension",
+   "fieldname": "dimensions",
+   "fieldtype": "Table",
+   "label": "Applicable Dimension",
+   "options": "Allowed Dimension",
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "disabled",
+   "fieldtype": "Check",
+   "label": "Disabled",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "dimension_filter_help",
+   "fieldtype": "HTML",
+   "label": "Dimension Filter Help",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "section_break_10",
+   "fieldtype": "Section Break",
+   "show_days": 1,
+   "show_seconds": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-02-03 12:04:58.678402",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Accounting Dimension Filter",
+ "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": "Accounts User",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Accounts Manager",
+   "share": 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/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py
new file mode 100644
index 0000000..6aef9ca
--- /dev/null
+++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py
@@ -0,0 +1,61 @@
+# -*- 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 import _, scrub
+from frappe.model.document import Document
+
+class AccountingDimensionFilter(Document):
+	def validate(self):
+		self.validate_applicable_accounts()
+
+	def validate_applicable_accounts(self):
+		accounts = frappe.db.sql(
+			"""
+				SELECT a.applicable_on_account as account
+				FROM `tabApplicable On Account` a, `tabAccounting Dimension Filter` d
+				WHERE d.name = a.parent
+				and d.name != %s
+				and d.accounting_dimension = %s
+			""", (self.name, self.accounting_dimension), as_dict=1)
+
+		account_list = [d.account for d in accounts]
+
+		for account in self.get('accounts'):
+			if account.applicable_on_account in account_list:
+				frappe.throw(_("Row {0}: {1} account already applied for Accounting Dimension {2}").format(
+					account.idx, frappe.bold(account.applicable_on_account), frappe.bold(self.accounting_dimension)))
+
+def get_dimension_filter_map():
+	filters = frappe.db.sql("""
+		SELECT
+			a.applicable_on_account, d.dimension_value, p.accounting_dimension,
+			p.allow_or_restrict, a.is_mandatory
+		FROM
+			`tabApplicable On Account` a, `tabAllowed Dimension` d,
+			`tabAccounting Dimension Filter` p
+		WHERE
+			p.name = a.parent
+			AND p.disabled = 0
+			AND p.name = d.parent
+	""", as_dict=1)
+
+	dimension_filter_map = {}
+
+	for f in filters:
+		f.fieldname = scrub(f.accounting_dimension)
+
+		build_map(dimension_filter_map, f.fieldname, f.applicable_on_account, f.dimension_value,
+			f.allow_or_restrict, f.is_mandatory)
+
+	return dimension_filter_map
+
+def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory):
+	map_object.setdefault((dimension, account), {
+		'allowed_dimensions': [],
+		'is_mandatory': is_mandatory,
+		'allow_or_restrict': allow_or_restrict
+	})
+	map_object[(dimension, account)]['allowed_dimensions'].append(filter_value)
diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py
new file mode 100644
index 0000000..7877abd
--- /dev/null
+++ b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import create_dimension, disable_dimension
+from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError
+
+class TestAccountingDimensionFilter(unittest.TestCase):
+	def setUp(self):
+		create_dimension()
+		create_accounting_dimension_filter()
+		self.invoice_list = []
+
+	def test_allowed_dimension_validation(self):
+		si = create_sales_invoice(do_not_save=1)
+		si.items[0].cost_center = 'Main - _TC'
+		si.department = 'Accounts - _TC'
+		si.location = 'Block 1'
+		si.save()
+
+		self.assertRaises(InvalidAccountDimensionError, si.submit)
+		self.invoice_list.append(si)
+
+	def test_mandatory_dimension_validation(self):
+		si = create_sales_invoice(do_not_save=1)
+		si.department = ''
+		si.location = 'Block 1'
+
+		# Test with no department for Sales Account
+		si.items[0].department = ''
+		si.items[0].cost_center = '_Test Cost Center 2 - _TC'
+		si.save()
+
+		self.assertRaises(MandatoryAccountDimensionError, si.submit)
+		self.invoice_list.append(si)
+
+	def tearDown(self):
+		disable_dimension_filter()
+		disable_dimension()
+
+		for si in self.invoice_list:
+			si.load_from_db()
+			if si.docstatus == 1:
+				si.cancel()
+
+def create_accounting_dimension_filter():
+	if not frappe.db.get_value('Accounting Dimension Filter',
+		{'accounting_dimension': 'Cost Center'}):
+		frappe.get_doc({
+			'doctype': 'Accounting Dimension Filter',
+			'accounting_dimension': 'Cost Center',
+			'allow_or_restrict': 'Allow',
+			'company': '_Test Company',
+			'accounts': [{
+				'applicable_on_account': 'Sales - _TC',
+			}],
+			'dimensions': [{
+				'accounting_dimension': 'Cost Center',
+				'dimension_value': '_Test Cost Center 2 - _TC'
+			}]
+		}).insert()
+	else:
+		doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Cost Center'})
+		doc.disabled = 0
+		doc.save()
+
+	if not frappe.db.get_value('Accounting Dimension Filter',
+		{'accounting_dimension': 'Department'}):
+		frappe.get_doc({
+			'doctype': 'Accounting Dimension Filter',
+			'accounting_dimension': 'Department',
+			'allow_or_restrict': 'Allow',
+			'company': '_Test Company',
+			'accounts': [{
+				'applicable_on_account': 'Sales - _TC',
+				'is_mandatory': 1
+			}],
+			'dimensions': [{
+				'accounting_dimension': 'Department',
+				'dimension_value': 'Accounts - _TC'
+			}]
+		}).insert()
+	else:
+		doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Department'})
+		doc.disabled = 0
+		doc.save()
+
+def disable_dimension_filter():
+	doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Cost Center'})
+	doc.disabled = 1
+	doc.save()
+
+	doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Department'})
+	doc.disabled = 1
+	doc.save()
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index 41f9ce0..a3c29b6 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -21,6 +21,7 @@
   "book_asset_depreciation_entry_automatically",
   "add_taxes_from_item_tax_template",
   "automatically_fetch_payment_terms",
+  "delete_linked_ledger_entries",
   "deferred_accounting_settings_section",
   "automatically_process_deferred_accounting_entry",
   "book_deferred_entries_based_on",
@@ -219,6 +220,12 @@
    "fieldtype": "Select",
    "label": "Book Deferred Entries Based On",
    "options": "Days\nMonths"
+  },
+  {
+   "default": "0",
+   "fieldname": "delete_linked_ledger_entries",
+   "fieldtype": "Check",
+   "label": "Delete Accounting and Stock Ledger Entries on deletion of Transaction"
   }
  ],
  "icon": "icon-cog",
@@ -226,7 +233,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2020-10-13 11:32:52.268826",
+ "modified": "2021-01-05 13:04:00.118892",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounts Settings",
@@ -254,4 +261,4 @@
  "sort_field": "modified",
  "sort_order": "ASC",
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/accounts/doctype/allowed_dimension/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/accounts/doctype/allowed_dimension/__init__.py
diff --git a/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.json b/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.json
new file mode 100644
index 0000000..7fe2a3c
--- /dev/null
+++ b/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.json
@@ -0,0 +1,43 @@
+{
+ "actions": [],
+ "creation": "2020-11-08 18:22:36.001131",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "accounting_dimension",
+  "dimension_value"
+ ],
+ "fields": [
+  {
+   "fieldname": "accounting_dimension",
+   "fieldtype": "Link",
+   "label": "Accounting Dimension",
+   "options": "DocType",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "dimension_value",
+   "fieldtype": "Dynamic Link",
+   "in_list_view": 1,
+   "options": "accounting_dimension",
+   "show_days": 1,
+   "show_seconds": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2020-11-23 09:56:19.744200",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Allowed Dimension",
+ "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/accounts/doctype/allowed_dimension/allowed_dimension.py b/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.py
new file mode 100644
index 0000000..c2afc1a
--- /dev/null
+++ b/erpnext/accounts/doctype/allowed_dimension/allowed_dimension.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 AllowedDimension(Document):
+	pass
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/accounts/doctype/applicable_on_account/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/accounts/doctype/applicable_on_account/__init__.py
diff --git a/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json b/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json
new file mode 100644
index 0000000..95e98d0
--- /dev/null
+++ b/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json
@@ -0,0 +1,46 @@
+{
+ "actions": [],
+ "creation": "2020-11-08 18:20:00.944449",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "applicable_on_account",
+  "is_mandatory"
+ ],
+ "fields": [
+  {
+   "fieldname": "applicable_on_account",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Accounts",
+   "options": "Account",
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "columns": 2,
+   "default": "0",
+   "fieldname": "is_mandatory",
+   "fieldtype": "Check",
+   "in_list_view": 1,
+   "label": "Is Mandatory",
+   "show_days": 1,
+   "show_seconds": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2020-11-22 19:55:13.324136",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Applicable On Account",
+ "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/accounts/doctype/applicable_on_account/applicable_on_account.py b/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.py
new file mode 100644
index 0000000..0fccaf3
--- /dev/null
+++ b/erpnext/accounts/doctype/applicable_on_account/applicable_on_account.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 ApplicableOnAccount(Document):
+	pass
diff --git a/erpnext/accounts/doctype/bank/bank.js b/erpnext/accounts/doctype/bank/bank.js
index de9498e..49b2b18 100644
--- a/erpnext/accounts/doctype/bank/bank.js
+++ b/erpnext/accounts/doctype/bank/bank.js
@@ -1,5 +1,6 @@
 // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
+frappe.provide('erpnext.integrations');
 
 frappe.ui.form.on('Bank', {
 	onload: function(frm) {
@@ -20,7 +21,12 @@
 			frm.set_df_property('address_and_contact', 'hidden', 0);
 			frappe.contacts.render_address_and_contact(frm);
 		}
-	},
+		if (frm.doc.plaid_access_token) {
+			frm.add_custom_button(__('Refresh Plaid Link'), () => {
+				new erpnext.integrations.refreshPlaidLink(frm.doc.plaid_access_token);
+			});
+		}
+	}
 });
 
 
@@ -40,4 +46,79 @@
 		frm.doc.name).options = options;
 
 	frm.fields_dict.bank_transaction_mapping.grid.refresh();
-};
\ No newline at end of file
+};
+
+erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
+	constructor(access_token) {
+		this.access_token = access_token;
+		this.plaidUrl = 'https://cdn.plaid.com/link/v2/stable/link-initialize.js';
+		this.init_config();
+	}
+
+	async init_config() {
+		this.plaid_env = await frappe.db.get_single_value('Plaid Settings', 'plaid_env');
+		this.token = await this.get_link_token_for_update();
+		this.init_plaid();
+	}
+
+	async get_link_token_for_update() {
+		const token = frappe.xcall(
+			'erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_link_token_for_update',
+			{ access_token: this.access_token }
+		)
+		if (!token) {
+			frappe.throw(__('Cannot retrieve link token for update. Check Error Log for more information'));
+		}
+		return token;
+	}
+
+	init_plaid() {
+		const me = this;
+		me.loadScript(me.plaidUrl)
+			.then(() => {
+				me.onScriptLoaded(me);
+			})
+			.then(() => {
+				if (me.linkHandler) {
+					me.linkHandler.open();
+				}
+			})
+			.catch((error) => {
+				me.onScriptError(error);
+			});
+	}
+
+	loadScript(src) {
+		return new Promise(function (resolve, reject) {
+			if (document.querySelector("script[src='" + src + "']")) {
+				resolve();
+				return;
+			}
+			const el = document.createElement('script');
+			el.type = 'text/javascript';
+			el.async = true;
+			el.src = src;
+			el.addEventListener('load', resolve);
+			el.addEventListener('error', reject);
+			el.addEventListener('abort', reject);
+			document.head.appendChild(el);
+		});
+	}
+
+	onScriptLoaded(me) {
+		me.linkHandler = Plaid.create({
+			env: me.plaid_env,
+			token: me.token,
+			onSuccess: me.plaid_success
+		});
+	}
+
+	onScriptError(error) {
+		frappe.msgprint(__("There was an issue connecting to Plaid's authentication server. Check browser console for more information"));
+		console.log(error);
+	}
+
+	plaid_success(token, response) {
+		frappe.show_alert({ message: __('Plaid Link Updated'), indicator: 'green' });
+	}
+};
diff --git a/erpnext/accounts/doctype/budget/budget.js b/erpnext/accounts/doctype/budget/budget.js
index cadf1e7..e162e32 100644
--- a/erpnext/accounts/doctype/budget/budget.js
+++ b/erpnext/accounts/doctype/budget/budget.js
@@ -1,24 +1,9 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
+frappe.provide("erpnext.accounts.dimensions");
 
 frappe.ui.form.on('Budget', {
 	onload: function(frm) {
-		frm.set_query("cost_center", function() {
-			return {
-				filters: {
-					company: frm.doc.company
-				}
-			}
-		})
-
-		frm.set_query("project", function() {
-			return {
-				filters: {
-					company: frm.doc.company
-				}
-			}
-		})
-		
 		frm.set_query("account", "accounts", function() {
 			return {
 				filters: {
@@ -26,16 +11,18 @@
 					report_type: "Profit and Loss",
 					is_group: 0
 				}
-			}
-		})
-		
+			};
+		});
+
 		frm.set_query("monthly_distribution", function() {
 			return {
 				filters: {
 					fiscal_year: frm.doc.fiscal_year
 				}
-			}
-		})
+			};
+		});
+
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
 	},
 
 	refresh: function(frm) {
diff --git a/erpnext/accounts/doctype/budget/test_budget.py b/erpnext/accounts/doctype/budget/test_budget.py
index 0f115f9..c5ec23c 100644
--- a/erpnext/accounts/doctype/budget/test_budget.py
+++ b/erpnext/accounts/doctype/budget/test_budget.py
@@ -122,8 +122,10 @@
 
 		frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
 
+		project = frappe.get_value("Project", {"project_name": "_Test Project"})
+
 		jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
-			"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", project="_Test Project", posting_date=nowdate())
+			"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", project=project, posting_date=nowdate())
 
 		self.assertRaises(BudgetError, jv.submit)
 
@@ -147,8 +149,11 @@
 
 		budget = make_budget(budget_against="Project")
 
+		project = frappe.get_value("Project", {"project_name": "_Test Project"})
+
 		jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
-			"_Test Bank - _TC", 250000, "_Test Cost Center - _TC", project="_Test Project", posting_date=nowdate())
+			"_Test Bank - _TC", 250000, "_Test Cost Center - _TC",
+			project=project, posting_date=nowdate())
 
 		self.assertRaises(BudgetError, jv.submit)
 
@@ -159,10 +164,10 @@
 
 		budget = make_budget(budget_against="Cost Center")
 		month = now_datetime().month
-		if month > 10:
-			month = 10
+		if month > 9:
+			month = 9
 
-		for i in range(month):
+		for i in range(month+1):
 			jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
 				"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
 
@@ -181,12 +186,14 @@
 
 		budget = make_budget(budget_against="Project")
 		month = now_datetime().month
-		if month > 10:
-			month = 10
+		if month > 9:
+			month = 9
 
-		for i in range(month):
+		project = frappe.get_value("Project", {"project_name": "_Test Project"})
+		for i in range(month + 1):
 			jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
-				"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True, project="_Test Project")
+				"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True,
+				project=project)
 
 			self.assertTrue(frappe.db.get_value("GL Entry",
 				{"voucher_type": "Journal Entry", "voucher_no": jv.name}))
@@ -289,7 +296,7 @@
 	budget = frappe.new_doc("Budget")
 
 	if budget_against == "Project":
-		budget.project = "_Test Project"
+		budget.project = frappe.get_value("Project", {"project_name": "_Test Project"})
 	else:
 		budget.cost_center =cost_center or "_Test Cost Center - _TC"
 
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index c441274..ce76d0a 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -11,8 +11,10 @@
 from erpnext.accounts.party import validate_party_gle_currency, validate_party_frozen_disabled
 from erpnext.accounts.utils import get_account_currency
 from erpnext.accounts.utils import get_fiscal_year
-from erpnext.exceptions import InvalidAccountCurrency
+from erpnext.exceptions import InvalidAccountCurrency, InvalidAccountDimensionError, MandatoryAccountDimensionError
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_checks_for_pl_and_bs_accounts
+from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import get_dimension_filter_map
+from six import iteritems
 
 exclude_from_linked_with = True
 class GLEntry(Document):
@@ -25,29 +27,30 @@
 
 	def validate(self):
 		self.flags.ignore_submit_comment = True
-		self.check_mandatory()
 		self.validate_and_set_fiscal_year()
 		self.pl_must_have_cost_center()
-		self.validate_cost_center()
 
 		if not self.flags.from_repost:
+			self.check_mandatory()
+			self.validate_cost_center()
 			self.check_pl_account()
 			self.validate_party()
 			self.validate_currency()
 
-	def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False):
-		if not from_repost:
+	def on_update(self):
+		adv_adj = self.flags.adv_adj
+		if not self.flags.from_repost:
 			self.validate_account_details(adv_adj)
 			self.validate_dimensions_for_pl_and_bs()
+			self.validate_allowed_dimensions()
+			validate_balance_type(self.account, adv_adj)
+			validate_frozen_account(self.account, adv_adj)
 
-		validate_frozen_account(self.account, adv_adj)
-		validate_balance_type(self.account, adv_adj)
-
-		# Update outstanding amt on against voucher
-		if self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees'] \
-			and self.against_voucher and update_outstanding == 'Yes' and not from_repost:
-				update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type,
-					self.against_voucher)
+			# Update outstanding amt on against voucher
+			if (self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees']
+				and self.against_voucher and self.flags.update_outstanding == 'Yes'):
+					update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type,
+						self.against_voucher)
 
 	def check_mandatory(self):
 		mandatory = ['account','voucher_type','voucher_no','company']
@@ -55,7 +58,7 @@
 			if not self.get(k):
 				frappe.throw(_("{0} is required").format(_(self.meta.get_label(k))))
 
-		account_type = frappe.db.get_value("Account", self.account, "account_type")
+		account_type = frappe.get_cached_value("Account", self.account, "account_type")
 		if not (self.party_type and self.party):
 			if account_type == "Receivable":
 				frappe.throw(_("{0} {1}: Customer is required against Receivable account {2}")
@@ -70,17 +73,15 @@
 				.format(self.voucher_type, self.voucher_no, self.account))
 
 	def pl_must_have_cost_center(self):
-		if frappe.db.get_value("Account", self.account, "report_type") == "Profit and Loss":
+		if frappe.get_cached_value("Account", self.account, "report_type") == "Profit and Loss":
 			if not self.cost_center and self.voucher_type != 'Period Closing Voucher':
 				frappe.throw(_("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}. Please set up a default Cost Center for the Company.")
 					.format(self.voucher_type, self.voucher_no, self.account))
 
 	def validate_dimensions_for_pl_and_bs(self):
-
 		account_type = frappe.db.get_value("Account", self.account, "report_type")
 
 		for dimension in get_checks_for_pl_and_bs_accounts():
-
 			if account_type == "Profit and Loss" \
 				and self.company == dimension.company and dimension.mandatory_for_pl and not dimension.disabled:
 				if not self.get(dimension.fieldname):
@@ -93,6 +94,25 @@
 					frappe.throw(_("Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}.")
 						.format(dimension.label, self.account))
 
+	def validate_allowed_dimensions(self):
+		dimension_filter_map = get_dimension_filter_map()
+		for key, value in iteritems(dimension_filter_map):
+			dimension = key[0]
+			account = key[1]
+
+			if self.account == account:
+				if value['is_mandatory'] and not self.get(dimension):
+					frappe.throw(_("{0} is mandatory for account {1}").format(
+						frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)), MandatoryAccountDimensionError)
+
+				if value['allow_or_restrict'] == 'Allow':
+					if self.get(dimension) and self.get(dimension) not in value['allowed_dimensions']:
+						frappe.throw(_("Invalid value {0} for {1} against account {2}").format(
+							frappe.bold(self.get(dimension)), frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)), InvalidAccountDimensionError)
+				else:
+					if self.get(dimension) and self.get(dimension) in value['allowed_dimensions']:
+						frappe.throw(_("Invalid value {0} for {1} against account {2}").format(
+							frappe.bold(self.get(dimension)), frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)), InvalidAccountDimensionError)
 
 	def check_pl_account(self):
 		if self.is_opening=='Yes' and \
@@ -120,26 +140,18 @@
 				.format(self.voucher_type, self.voucher_no, self.account, self.company))
 
 	def validate_cost_center(self):
-		if not hasattr(self, "cost_center_company"):
-			self.cost_center_company = {}
+		if not self.cost_center: return
 
-		def _get_cost_center_company():
-			if not self.cost_center_company.get(self.cost_center):
-				self.cost_center_company[self.cost_center] = frappe.db.get_value(
-					"Cost Center", self.cost_center, "company")
+		is_group, company = frappe.get_cached_value('Cost Center',
+			self.cost_center, ['is_group', 'company'])
 
-			return self.cost_center_company[self.cost_center]
-
-		def _check_is_group():
-			return cint(frappe.get_cached_value('Cost Center', self.cost_center, 'is_group'))
-
-		if self.cost_center and _get_cost_center_company() != self.company:
+		if company != self.company:
 			frappe.throw(_("{0} {1}: Cost Center {2} does not belong to Company {3}")
 				.format(self.voucher_type, self.voucher_no, self.cost_center, self.company))
 
-		if self.cost_center and _check_is_group():
-			frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions""")
-				.format(self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
+		if (self.voucher_type != 'Period Closing Voucher' and is_group):
+			frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions""").format(
+				self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
 
 	def validate_party(self):
 		validate_party_frozen_disabled(self.party_type, self.party)
@@ -149,7 +161,7 @@
 		account_currency = get_account_currency(self.account)
 
 		if not self.account_currency:
-			self.account_currency = company_currency
+			self.account_currency = account_currency or company_currency
 
 		if account_currency != self.account_currency:
 			frappe.throw(_("{0} {1}: Accounting Entry for {2} can only be made in currency: {3}")
@@ -163,7 +175,6 @@
 		if not self.fiscal_year:
 			self.fiscal_year = get_fiscal_year(self.posting_date, company=self.company)[0]
 
-
 def validate_balance_type(account, adv_adj=False):
 	if not adv_adj and account:
 		balance_must_be = frappe.db.get_value("Account", account, "balance_must_be")
@@ -229,7 +240,7 @@
 
 
 def validate_frozen_account(account, adv_adj=None):
-	frozen_account = frappe.db.get_value("Account", account, "freeze_account")
+	frozen_account = frappe.get_cached_value("Account", account, "freeze_account")
 	if frozen_account == 'Yes' and not adv_adj:
 		frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,
 			'frozen_accounts_modifier')
diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template_dashboard.py b/erpnext/accounts/doctype/item_tax_template/item_tax_template_dashboard.py
index acc308e..3d80a97 100644
--- a/erpnext/accounts/doctype/item_tax_template/item_tax_template_dashboard.py
+++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template_dashboard.py
@@ -20,7 +20,8 @@
 				'items': ['Purchase Invoice', 'Purchase Order', 'Purchase Receipt']
 			},
 			{
-				'items': ['Item']
+				'label': _('Stock'),
+				'items': ['Item Groups', 'Item']
 			}
 		]
 	}
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index ff12967..37b03f3 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -120,6 +120,8 @@
 				}
 			}
 		});
+
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
 	},
 
 	voucher_type: function(frm){
@@ -197,6 +199,7 @@
 		this.load_defaults();
 		this.setup_queries();
 		this.setup_balance_formatter();
+		erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
 	},
 
 	onload_post_render: function() {
@@ -222,15 +225,6 @@
 			return erpnext.journal_entry.account_query(me.frm);
 		});
 
-		me.frm.set_query("cost_center", "accounts", function(doc, cdt, cdn) {
-			return {
-				filters: {
-					company: me.frm.doc.company,
-					is_group: 0
-				}
-			};
-		});
-
 		me.frm.set_query("party_type", "accounts", function(doc, cdt, cdn) {
 			const row = locals[cdt][cdn];
 
@@ -406,6 +400,8 @@
 			}
 		}
 		cur_frm.cscript.update_totals(doc);
+
+		erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'accounts');
 	},
 
 });
diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
index b56f8e5..5f003e0 100644
--- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
@@ -160,7 +160,7 @@
 		self.assertFalse(gle)
 
 	def test_reverse_journal_entry(self):
-		from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry 
+		from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
 		jv = make_journal_entry("_Test Bank USD - _TC",
 			"Sales - _TC", 100, exchange_rate=50, save=False)
 
@@ -299,15 +299,20 @@
 
 	def test_jv_with_project(self):
 		from erpnext.projects.doctype.project.test_project import make_project
-		project = make_project({
-			'project_name': 'Journal Entry Project',
-			'project_template_name': 'Test Project Template',
-			'start_date': '2020-01-01'
-		})
+
+		if not frappe.db.exists("Project", {"project_name": "Journal Entry Project"}):
+			project = make_project({
+				'project_name': 'Journal Entry Project',
+				'project_template_name': 'Test Project Template',
+				'start_date': '2020-01-01'
+			})
+			project_name = project.name
+		else:
+			project_name = frappe.get_value("Project", {"project_name": "_Test Project"})
 
 		jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, save=False)
 		for d in jv.accounts:
-			d.project = project.project_name
+			d.project = project_name
 		jv.voucher_type = "Bank Entry"
 		jv.multi_currency = 0
 		jv.cheque_no = "112233"
@@ -317,10 +322,10 @@
 
 		expected_values = {
 			"_Test Cash - _TC": {
-				"project": project.project_name
+				"project": project_name
 			},
 			"_Test Bank - _TC": {
-				"project": project.project_name
+				"project": project_name
 			}
 		}
 
diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.js b/erpnext/accounts/doctype/loyalty_program/loyalty_program.js
index 524a671..f90f867 100644
--- a/erpnext/accounts/doctype/loyalty_program/loyalty_program.js
+++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.js
@@ -1,6 +1,8 @@
 // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 
+frappe.provide("erpnext.accounts.dimensions");
+
 frappe.ui.form.on('Loyalty Program', {
 	setup: function(frm) {
 		var help_content =
@@ -46,20 +48,17 @@
 			};
 		});
 
-		frm.set_query("cost_center", function() {
-			return {
-				filters: {
-					company: frm.doc.company
-				}
-			};
-		});
-
 		frm.set_value("company", frappe.defaults.get_user_default("Company"));
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
 	},
 
 	refresh: function(frm) {
 		if (frm.doc.loyalty_program_type === "Single Tier Program" && frm.doc.collection_rules.length > 1) {
 			frappe.throw(__("Please select the Multiple Tier Program type for more than one collection rules."));
 		}
+	},
+
+	company: function(frm) {
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
 	}
 });
diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js
index 3ce5701..c087980 100644
--- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js
+++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js
@@ -36,6 +36,8 @@
 			frm.dashboard.show_progress(data.title, (data.count / data.total) * 100, data.message);
 			frm.page.set_indicator(__('In Progress'), 'orange');
 		});
+
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
 	},
 
 	refresh: function(frm) {
@@ -100,6 +102,7 @@
 				}
 			})
 		}
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
 	},
 
 	invoice_type: function(frm) {
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index e117471..6412772 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -1,6 +1,7 @@
 // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 {% include "erpnext/public/js/controllers/accounts.js" %}
+frappe.provide("erpnext.accounts.dimensions");
 
 frappe.ui.form.on('Payment Entry', {
 	onload: function(frm) {
@@ -8,6 +9,8 @@
 			if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null);
 			if (!frm.doc.paid_to) frm.set_value("paid_to_account_currency", null);
 		}
+
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
 	},
 
 	setup: function(frm) {
@@ -88,24 +91,17 @@
 			}
 		});
 
-		frm.set_query("cost_center", "deductions", function() {
-			return {
-				filters: {
-					"is_group": 0,
-					"company": frm.doc.company
-				}
-			}
-		});
-
 		frm.set_query("reference_doctype", "references", function() {
-			if (frm.doc.party_type=="Customer") {
+			if (frm.doc.party_type == "Customer") {
 				var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"];
-			} else if (frm.doc.party_type=="Supplier") {
+			} else if (frm.doc.party_type == "Supplier") {
 				var doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"];
-			} else if (frm.doc.party_type=="Employee") {
+			} else if (frm.doc.party_type == "Employee") {
 				var doctypes = ["Expense Claim", "Journal Entry"];
-			} else if (frm.doc.party_type=="Student") {
+			} else if (frm.doc.party_type == "Student") {
 				var doctypes = ["Fees"];
+			} else if (frm.doc.party_type == "Donor") {
+				var doctypes = ["Donation"];
 			} else {
 				var doctypes = ["Journal Entry"];
 			}
@@ -134,7 +130,7 @@
 			const child = locals[cdt][cdn];
 			const filters = {"docstatus": 1, "company": doc.company};
 			const party_type_doctypes = ['Sales Invoice', 'Sales Order', 'Purchase Invoice',
-				'Purchase Order', 'Expense Claim', 'Fees', 'Dunning'];
+				'Purchase Order', 'Expense Claim', 'Fees', 'Dunning', 'Donation'];
 
 			if (in_list(party_type_doctypes, child.reference_doctype)) {
 				filters[doc.party_type.toLowerCase()] = doc.party;
@@ -167,6 +163,7 @@
 	company: function(frm) {
 		frm.events.hide_unhide_fields(frm);
 		frm.events.set_dynamic_labels(frm);
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
 	},
 
 	contact_person: function(frm) {
@@ -286,7 +283,7 @@
 		let party_types = Object.keys(frappe.boot.party_account_types);
 		if(frm.doc.party_type && !party_types.includes(frm.doc.party_type)){
 			frm.set_value("party_type", "");
-			frappe.throw(__("Party can only be one of "+ party_types.join(", ")));
+			frappe.throw(__("Party can only be one of {0}", [party_types.join(", ")]));
 		}
 
 		frm.set_query("party", function() {
@@ -401,6 +398,8 @@
 
 	set_account_currency_and_balance: function(frm, account, currency_field,
 			balance_field, callback_function) {
+
+		var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
 		if (frm.doc.posting_date && account) {
 			frappe.call({
 				method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_account_details",
@@ -427,6 +426,14 @@
 
 									if(!frm.doc.paid_amount && frm.doc.received_amount)
 										frm.events.received_amount(frm);
+
+									if (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency
+										&& frm.doc.paid_amount != frm.doc.received_amount) {
+											if (company_currency != frm.doc.paid_from_account_currency &&
+												frm.doc.payment_type == "Pay") {
+													frm.doc.paid_amount = frm.doc.received_amount;
+												}
+										}
 								}
 							},
 							() => {
@@ -700,7 +707,8 @@
 						(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") ||
 						(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier")  ||
 						(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Employee") ||
-						(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student")
+						(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student") ||
+						(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Donor")
 					) {
 						if(total_positive_outstanding > total_negative_outstanding)
 							if (!frm.doc.paid_amount)
@@ -743,7 +751,8 @@
 				(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") ||
 				(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier") ||
 				(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Employee") ||
-				(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student")
+				(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student") ||
+				(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Donor")
 			) {
 				if(total_positive_outstanding_including_order > paid_amount) {
 					var remaining_outstanding = total_positive_outstanding_including_order - paid_amount;
@@ -900,6 +909,12 @@
 				frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Expense Claim or Journal Entry", [row.idx]));
 				return false;
 			}
+
+			if (frm.doc.party_type == "Donor" && row.reference_doctype != "Donation") {
+				frappe.model.set_value(row.doctype, row.name, "reference_doctype", null);
+				frappe.msgprint(__("Row #{0}: Reference Document Type must be Donation", [row.idx]));
+				return false;
+			}
 		}
 
 		if (row) {
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 31a4c8a..203d06a 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -72,6 +72,7 @@
 		self.update_outstanding_amounts()
 		self.update_advance_paid()
 		self.update_expense_claim()
+		self.update_donation()
 		self.update_payment_schedule()
 		self.set_status()
 
@@ -82,6 +83,7 @@
 		self.update_outstanding_amounts()
 		self.update_advance_paid()
 		self.update_expense_claim()
+		self.update_donation(cancel=1)
 		self.delink_advance_entry_references()
 		self.update_payment_schedule(cancel=1)
 		self.set_payment_req_status()
@@ -245,6 +247,8 @@
 			valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance")
 		elif self.party_type == "Shareholder":
 			valid_reference_doctypes = ("Journal Entry")
+		elif self.party_type == "Donor":
+			valid_reference_doctypes = ("Donation")
 
 		for d in self.get("references"):
 			if not d.allocated_amount:
@@ -614,6 +618,13 @@
 					doc = frappe.get_doc("Expense Claim", d.reference_name)
 					update_reimbursed_amount(doc, self.name)
 
+	def update_donation(self, cancel=0):
+		if self.payment_type == "Receive" and self.party_type == "Donor" and self.party:
+			for d in self.get("references"):
+				if d.reference_doctype=="Donation" and d.reference_name:
+					is_paid = 0 if cancel else 1
+					frappe.db.set_value("Donation", d.reference_name, "paid", is_paid)
+
 	def on_recurring(self, reference_doc, auto_repeat_doc):
 		self.reference_no = reference_doc.name
 		self.reference_date = nowdate()
@@ -913,6 +924,9 @@
 		total_amount = ref_doc.get("grand_total")
 		exchange_rate = 1
 		outstanding_amount = ref_doc.get("outstanding_amount")
+	elif reference_doctype == "Donation":
+		total_amount = ref_doc.get("amount")
+		exchange_rate = 1
 	elif reference_doctype == "Dunning":
 		total_amount = ref_doc.get("dunning_amount")
 		exchange_rate = 1
@@ -1162,8 +1176,10 @@
 		party_type = "Supplier"
 	elif dt in ("Expense Claim", "Employee Advance"):
 		party_type = "Employee"
-	elif dt in ("Fees"):
+	elif dt == "Fees":
 		party_type = "Student"
+	elif dt == "Donation":
+		party_type = "Donor"
 	return party_type
 
 def set_party_account(dt, dn, doc, party_type):
@@ -1189,7 +1205,7 @@
 	return party_account_currency
 
 def set_payment_type(dt, doc):
-	if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees", "Dunning") and doc.outstanding_amount > 0)) \
+	if (dt in ("Sales Order", "Donation") or (dt in ("Sales Invoice", "Fees", "Dunning") and doc.outstanding_amount > 0)) \
 		or (dt=="Purchase Invoice" and doc.outstanding_amount < 0):
 			payment_type = "Receive"
 	else:
@@ -1222,6 +1238,9 @@
 	elif dt == "Dunning":
 		grand_total = doc.grand_total
 		outstanding_amount = doc.grand_total
+	elif dt == "Donation":
+		grand_total = doc.amount
+		outstanding_amount = doc.amount
 	else:
 		if party_account_currency == doc.company_currency:
 			grand_total = flt(doc.get("base_rounded_total") or doc.base_grand_total)
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index 1b97050..53ac996 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -3,6 +3,7 @@
 # For license information, please see license.txt
 
 from __future__ import unicode_literals
+import json
 import frappe
 from frappe import _
 from frappe.model.document import Document
@@ -82,18 +83,37 @@
 			self.make_communication_entry()
 
 		elif self.payment_channel == "Phone":
-			controller = get_payment_gateway_controller(self.payment_gateway)
-			payment_record = dict(
-				reference_doctype="Payment Request",
-				reference_docname=self.name,
-				payment_reference=self.reference_name,
-				grand_total=self.grand_total,
-				sender=self.email_to,
-				currency=self.currency,
-				payment_gateway=self.payment_gateway
-			)
-			controller.validate_transaction_currency(self.currency)
-			controller.request_for_payment(**payment_record)
+			self.request_phone_payment()
+
+	def request_phone_payment(self):
+		controller = get_payment_gateway_controller(self.payment_gateway)
+		request_amount = self.get_request_amount()
+
+		payment_record = dict(
+			reference_doctype="Payment Request",
+			reference_docname=self.name,
+			payment_reference=self.reference_name,
+			request_amount=request_amount,
+			sender=self.email_to,
+			currency=self.currency,
+			payment_gateway=self.payment_gateway
+		)
+
+		controller.validate_transaction_currency(self.currency)
+		controller.request_for_payment(**payment_record)
+	
+	def get_request_amount(self):
+		data_of_completed_requests = frappe.get_all("Integration Request", filters={
+			'reference_doctype': self.doctype,
+			'reference_docname': self.name,
+			'status': 'Completed'
+		}, pluck="data")
+
+		if not data_of_completed_requests:
+			return self.grand_total
+
+		request_amounts = sum([json.loads(d).get('request_amount') for d in data_of_completed_requests])
+		return request_amounts
 
 	def on_cancel(self):
 		self.check_if_payment_entry_exists()
@@ -351,8 +371,8 @@
 		if args.order_type == "Shopping Cart" or args.mute_email:
 			pr.flags.mute_email = True
 
+		pr.insert(ignore_permissions=True)
 		if args.submit_doc:
-			pr.insert(ignore_permissions=True)
 			pr.submit()
 
 	if args.order_type == "Shopping Cart":
@@ -412,8 +432,8 @@
 
 def get_gateway_details(args):
 	"""return gateway and payment account of default payment gateway"""
-	if args.get("payment_gateway"):
-		return get_payment_gateway_account(args.get("payment_gateway"))
+	if args.get("payment_gateway_account"):
+		return get_payment_gateway_account(args.get("payment_gateway_account"))
 
 	if args.order_type == "Shopping Cart":
 		payment_gateway_account = frappe.get_doc("Shopping Cart Settings").payment_gateway_account
diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py
index 8a10e2c..5eba62c 100644
--- a/erpnext/accounts/doctype/payment_request/test_payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py
@@ -45,7 +45,8 @@
 
 	def test_payment_request_linkings(self):
 		so_inr = make_sales_order(currency="INR")
-		pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com")
+		pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com",
+			payment_gateway_account="_Test Gateway - INR")
 
 		self.assertEqual(pr.reference_doctype, "Sales Order")
 		self.assertEqual(pr.reference_name, so_inr.name)
@@ -54,7 +55,8 @@
 		conversion_rate = get_exchange_rate("USD", "INR")
 
 		si_usd = create_sales_invoice(currency="USD", conversion_rate=conversion_rate)
-		pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com")
+		pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com",
+			payment_gateway_account="_Test Gateway - USD")
 
 		self.assertEqual(pr.reference_doctype, "Sales Invoice")
 		self.assertEqual(pr.reference_name, si_usd.name)
@@ -68,7 +70,7 @@
 
 		so_inr = make_sales_order(currency="INR")
 		pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com",
-			mute_email=1, submit_doc=1, return_doc=1)
+			mute_email=1, payment_gateway_account="_Test Gateway - INR", submit_doc=1, return_doc=1)
 		pe = pr.set_as_paid()
 
 		so_inr = frappe.get_doc("Sales Order", so_inr.name)
@@ -79,7 +81,7 @@
 			currency="USD", conversion_rate=50)
 
 		pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com",
-			mute_email=1, payment_gateway="_Test Gateway - USD", submit_doc=1, return_doc=1)
+			mute_email=1, payment_gateway_account="_Test Gateway - USD", submit_doc=1, return_doc=1)
 
 		pe = pr.set_as_paid()
 
@@ -106,7 +108,7 @@
 			currency="USD", conversion_rate=50)
 
 		pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com",
-			mute_email=1, payment_gateway="_Test Gateway - USD", submit_doc=1, return_doc=1)
+			mute_email=1, payment_gateway_account="_Test Gateway - USD", submit_doc=1, return_doc=1)
 
 		pe = pr.create_payment_entry()
 		pr.load_from_db()
diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
index 7dd5b01..a74fa06 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
+++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
@@ -8,7 +8,7 @@
 from erpnext.accounts.utils import get_account_currency
 from erpnext.controllers.accounts_controller import AccountsController
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (get_accounting_dimensions,
-	get_dimension_filters)
+	get_dimensions)
 
 class PeriodClosingVoucher(AccountsController):
 	def validate(self):
@@ -58,7 +58,7 @@
 		for dimension in accounting_dimensions:
 			dimension_fields.append('t1.{0}'.format(dimension))
 
-		dimension_filters, default_dimensions = get_dimension_filters()
+		dimension_filters, default_dimensions = get_dimensions()
 
 		pl_accounts = self.get_pl_balances(dimension_fields)
 
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
index 57baac7..9ea616f 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
@@ -3,6 +3,7 @@
 
 frappe.ui.form.on('POS Closing Entry', {
 	onload: function(frm) {
+		frm.ignore_doctypes_on_cancel_all = ['POS Invoice Merge Log'];
 		frm.set_query("pos_profile", function(doc) {
 			return {
 				filters: { 'user': doc.user }
@@ -20,7 +21,7 @@
 			return { filters: { 'status': 'Open', 'docstatus': 1 } };
 		});
 		
-		if (frm.doc.docstatus === 0) frm.set_value("period_end_date", frappe.datetime.now_datetime());
+		if (frm.doc.docstatus === 0 && !frm.doc.amended_from) frm.set_value("period_end_date", frappe.datetime.now_datetime());
 		if (frm.doc.docstatus === 1) set_html_data(frm);
 	},
 
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
index 32bca3b..18d430f 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
@@ -11,6 +11,7 @@
   "column_break_3",
   "posting_date",
   "pos_opening_entry",
+  "status",
   "section_break_5",
   "company",
   "column_break_7",
@@ -184,11 +185,27 @@
    "label": "POS Opening Entry",
    "options": "POS Opening Entry",
    "reqd": 1
+  },
+  {
+   "allow_on_submit": 1,
+   "default": "Draft",
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "hidden": 1,
+   "label": "Status",
+   "options": "Draft\nSubmitted\nQueued\nCancelled",
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "is_submittable": 1,
- "links": [],
- "modified": "2020-05-29 15:03:22.226113",
+ "links": [
+  {
+   "link_doctype": "POS Invoice Merge Log",
+   "link_fieldname": "pos_closing_entry"
+  }
+ ],
+ "modified": "2021-01-12 12:21:05.388650",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Closing Entry",
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
index 2b91c74..edf3d5a 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
@@ -6,13 +6,12 @@
 import frappe
 import json
 from frappe import _
-from frappe.model.document import Document
-from frappe.utils import getdate, get_datetime, flt
-from collections import defaultdict
+from frappe.utils import get_datetime, flt
+from erpnext.controllers.status_updater import StatusUpdater
 from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data
-from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices
+from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import consolidate_pos_invoices, unconsolidate_pos_invoices
 
-class POSClosingEntry(Document):
+class POSClosingEntry(StatusUpdater):
 	def validate(self):
 		if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open":
 			frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry"))
@@ -57,20 +56,29 @@
 		if not invalid_rows:
 			return
 
-		error_list = [_("Row #{}: {}").format(row.get('idx'), row.get('msg')) for row in invalid_rows]
-		frappe.throw(error_list, title=_("Invalid POS Invoices"), as_list=True)
+		error_list = []
+		for row in invalid_rows:
+			for msg in row.get('msg'):
+				error_list.append(_("Row #{}: {}").format(row.get('idx'), msg))
 
-	def on_submit(self):
-		merge_pos_invoices(self.pos_transactions)
-		opening_entry = frappe.get_doc("POS Opening Entry", self.pos_opening_entry)
-		opening_entry.pos_closing_entry = self.name
-		opening_entry.set_status()
-		opening_entry.save()
+		frappe.throw(error_list, title=_("Invalid POS Invoices"), as_list=True)
 
 	def get_payment_reconciliation_details(self):
 		currency = frappe.get_cached_value('Company', self.company,  "default_currency")
 		return frappe.render_template("erpnext/accounts/doctype/pos_closing_entry/closing_voucher_details.html",
 			{"data": self, "currency": currency})
+	
+	def on_submit(self):
+		consolidate_pos_invoices(closing_entry=self)
+	
+	def on_cancel(self):
+		unconsolidate_pos_invoices(closing_entry=self)
+
+	def update_opening_entry(self, for_cancel=False):
+		opening_entry = frappe.get_doc("POS Opening Entry", self.pos_opening_entry)
+		opening_entry.pos_closing_entry = self.name if not for_cancel else None
+		opening_entry.set_status()
+		opening_entry.save()
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry_list.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry_list.js
new file mode 100644
index 0000000..20fd610
--- /dev/null
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry_list.js
@@ -0,0 +1,16 @@
+// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+// render
+frappe.listview_settings['POS Closing Entry'] = {
+	get_indicator: function(doc) {
+		var status_color = {
+			"Draft": "red",
+			"Submitted": "blue",
+			"Queued": "orange",
+			"Cancelled": "red"
+
+		};
+		return [__(doc.status), status_color[doc.status], "status,=,"+doc.status];
+	}
+};
diff --git a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py
index 8de54d5..40db09e 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py
+++ b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py
@@ -13,7 +13,6 @@
 class TestPOSClosingEntry(unittest.TestCase):
 	def test_pos_closing_entry(self):
 		test_user, pos_profile = init_user_and_profile()
-
 		opening_entry = create_opening_entry(pos_profile, test_user.name)
 
 		pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1)
@@ -45,6 +44,49 @@
 		frappe.set_user("Administrator")
 		frappe.db.sql("delete from `tabPOS Profile`")
 
+	def test_cancelling_of_pos_closing_entry(self):
+		test_user, pos_profile = init_user_and_profile()
+		opening_entry = create_opening_entry(pos_profile, test_user.name)
+
+		pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1)
+		pos_inv1.append('payments', {
+			'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3500
+		})
+		pos_inv1.submit()
+
+		pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
+		pos_inv2.append('payments', {
+			'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3200
+		})
+		pos_inv2.submit()
+
+		pcv_doc = make_closing_entry_from_opening(opening_entry)
+		payment = pcv_doc.payment_reconciliation[0]
+
+		self.assertEqual(payment.mode_of_payment, 'Cash')
+
+		for d in pcv_doc.payment_reconciliation:
+			if d.mode_of_payment == 'Cash':
+				d.closing_amount = 6700
+
+		pcv_doc.submit()
+
+		pos_inv1.load_from_db()
+		self.assertRaises(frappe.ValidationError, pos_inv1.cancel)
+
+		si_doc = frappe.get_doc("Sales Invoice", pos_inv1.consolidated_invoice)
+		self.assertRaises(frappe.ValidationError, si_doc.cancel)
+
+		pcv_doc.load_from_db()
+		pcv_doc.cancel()
+		si_doc.load_from_db()
+		pos_inv1.load_from_db()
+		self.assertEqual(si_doc.docstatus, 2)
+		self.assertEqual(pos_inv1.status, 'Paid')
+
+		frappe.set_user("Administrator")
+		frappe.db.sql("delete from `tabPOS Profile`")
+
 def init_user_and_profile(**args):
 	user = 'test@example.com'
 	test_user = frappe.get_doc('User', user)
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
index 86062d1..493bd44 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
@@ -2,6 +2,7 @@
 // For license information, please see license.txt
 
 {% include 'erpnext/selling/sales_common.js' %};
+frappe.provide("erpnext.accounts");
 
 erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend({
 	setup(doc) {
@@ -9,12 +10,19 @@
 		this._super(doc);
 	},
 
+	company: function() {
+		erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
+	},
+
 	onload(doc) {
 		this._super();
+		this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice Merge Log'];
 		if(doc.__islocal && doc.is_pos && frappe.get_route_str() !== 'point-of-sale') {
 			this.frm.script_manager.trigger("is_pos");
 			this.frm.refresh_fields();
 		}
+
+		erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
 	},
 
 	refresh(doc) {
@@ -187,18 +195,43 @@
 	},
 
 	request_for_payment: function (frm) {
+		if (!frm.doc.contact_mobile) {
+			frappe.throw(__('Please enter mobile number first.'));
+		}
+		frm.dirty();
 		frm.save().then(() => {
-			frappe.dom.freeze();
-			frappe.call({
-				method: 'create_payment_request',
-				doc: frm.doc,
-			})
+			frappe.dom.freeze(__('Waiting for payment...'));
+			frappe
+				.call({
+					method: 'create_payment_request',
+					doc: frm.doc
+				})
 				.fail(() => {
 					frappe.dom.unfreeze();
-					frappe.msgprint('Payment request failed');
+					frappe.msgprint(__('Payment request failed'));
 				})
-				.then(() => {
-					frappe.msgprint('Payment request sent successfully');
+				.then(({ message }) => {
+					const payment_request_name = message.name;
+					setTimeout(() => {
+						frappe.db.get_value('Payment Request', payment_request_name, ['status', 'grand_total']).then(({ message }) => {
+							if (message.status != 'Paid') {
+								frappe.dom.unfreeze();
+								frappe.msgprint({
+									message: __('Payment Request took too long to respond. Please try requesting for payment again.'),
+									title: __('Request Timeout')
+								});
+							} else if (frappe.dom.freeze_count != 0) {
+								frappe.dom.unfreeze();
+								cur_frm.reload_doc();
+								cur_pos.payment.events.submit_invoice();
+
+								frappe.show_alert({
+									message: __("Payment of {0} received successfully.", [format_currency(message.grand_total, frm.doc.currency, 0)]),
+									indicator: 'green'
+								});
+							}
+						});
+					}, 60000);
 				});
 		});
 	}
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index a3fcd0c..0c1406c 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -6,10 +6,9 @@
 import frappe
 from frappe import _
 from frappe.model.document import Document
-from erpnext.controllers.selling_controller import SellingController
-from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate
 from erpnext.accounts.utils import get_account_currency
 from erpnext.accounts.party import get_party_account, get_due_date
+from frappe.utils import cint, flt, getdate, nowdate, get_link_to_form
 from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
 from erpnext.accounts.doctype.loyalty_program.loyalty_program import validate_loyalty_points
 from erpnext.stock.doctype.serial_no.serial_no import get_pos_reserved_serial_nos, get_serial_nos
@@ -58,6 +57,22 @@
 			self.apply_loyalty_points()
 		self.check_phone_payments()
 		self.set_status(update=True)
+	
+	def before_cancel(self):
+		if self.consolidated_invoice and frappe.db.get_value('Sales Invoice', self.consolidated_invoice, 'docstatus') == 1:
+			pos_closing_entry = frappe.get_all(
+				"POS Invoice Reference",
+				ignore_permissions=True,
+				filters={ 'pos_invoice': self.name },
+				pluck="parent",
+				limit=1
+			)
+			frappe.throw(
+				_('You need to cancel POS Closing Entry {} to be able to cancel this document.').format(
+					get_link_to_form("POS Closing Entry", pos_closing_entry[0])
+				),
+				title=_('Not Allowed')
+			)
 
 	def on_cancel(self):
 		# run on cancel method of selling controller
@@ -78,7 +93,7 @@
 						mode_of_payment=pay.mode_of_payment, status="Paid"),
 					fieldname="grand_total")
 
-				if pay.amount != paid_amt:
+				if paid_amt and pay.amount != paid_amt:
 					return frappe.throw(_("Payment related to {0} is not completed").format(pay.mode_of_payment))
 
 	def validate_stock_availablility(self):
@@ -266,6 +281,8 @@
 		from erpnext.stock.get_item_details import get_pos_profile_item_details, get_pos_profile
 		if not self.pos_profile:
 			pos_profile = get_pos_profile(self.company) or {}
+			if not pos_profile:
+				frappe.throw(_("No POS Profile found. Please create a New POS Profile first"))
 			self.pos_profile = pos_profile.get('name')
 
 		profile = {}
@@ -294,14 +311,21 @@
 						self.set(fieldname, profile.get(fieldname))
 
 			if self.customer:
-				customer_price_list, customer_group = frappe.db.get_value("Customer", self.customer, ['default_price_list', 'customer_group'])
+				customer_price_list, customer_group, customer_currency = frappe.db.get_value(
+					"Customer", self.customer, ['default_price_list', 'customer_group', 'default_currency']
+				)
 				customer_group_price_list = frappe.db.get_value("Customer Group", customer_group, 'default_price_list')
 				selling_price_list = customer_price_list or customer_group_price_list or profile.get('selling_price_list')
+				if customer_currency != profile.get('currency'):
+					self.set('currency', customer_currency)
+
 			else:
 				selling_price_list = profile.get('selling_price_list')
 
 			if selling_price_list:
 				self.set('selling_price_list', selling_price_list)
+			if customer_currency != profile.get('currency'):
+				self.set('currency', customer_currency)
 
 			# set pos values in items
 			for item in self.get("items"):
@@ -361,22 +385,48 @@
 				if not self.contact_mobile:
 					frappe.throw(_("Please enter the phone number first"))
 
-				payment_gateway = frappe.db.get_value("Payment Gateway Account", {
-					"payment_account": pay.account,
-				})
-				record = {
-					"payment_gateway": payment_gateway,
-					"dt": "POS Invoice",
-					"dn": self.name,
-					"payment_request_type": "Inward",
-					"party_type": "Customer",
-					"party": self.customer,
-					"mode_of_payment": pay.mode_of_payment,
-					"recipient_id": self.contact_mobile,
-					"submit_doc": True
-				}
+				pay_req = self.get_existing_payment_request(pay)
+				if not pay_req:
+					pay_req = self.get_new_payment_request(pay)
+					pay_req.submit()
+				else:
+					pay_req.request_phone_payment()
 
-				return make_payment_request(**record)
+				return pay_req
+	
+	def get_new_payment_request(self, mop):
+		payment_gateway_account = frappe.db.get_value("Payment Gateway Account", {
+			"payment_account": mop.account,
+		}, ["name"])
+
+		args = {
+			"dt": "POS Invoice",
+			"dn": self.name,
+			"recipient_id": self.contact_mobile,
+			"mode_of_payment": mop.mode_of_payment,
+			"payment_gateway_account": payment_gateway_account,
+			"payment_request_type": "Inward",
+			"party_type": "Customer",
+			"party": self.customer,
+			"return_doc": True
+		}
+		return make_payment_request(**args)
+
+	def get_existing_payment_request(self, pay):
+		payment_gateway_account = frappe.db.get_value("Payment Gateway Account", {
+			"payment_account": pay.account,
+		}, ["name"])
+
+		args = {
+			'doctype': 'Payment Request',
+			'reference_doctype': 'POS Invoice',
+			'reference_name': self.name,
+			'payment_gateway_account': payment_gateway_account,
+			'email_to': self.contact_mobile
+		}
+		pr = frappe.db.exists(args)
+		if pr:
+			return frappe.get_doc('Payment Request', pr[0][0])
 
 def add_return_modes(doc, pos_profile):
 	def append_payment(payment_mode):
diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
index c179360..57a23af 100644
--- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
@@ -290,7 +290,7 @@
 
 	def test_merging_into_sales_invoice_with_discount(self):
 		from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
-		from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices
+		from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import consolidate_pos_invoices
 
 		frappe.db.sql("delete from `tabPOS Invoice`")
 		test_user, pos_profile = init_user_and_profile()
@@ -306,7 +306,7 @@
 		})
 		pos_inv2.submit()
 
-		merge_pos_invoices()
+		consolidate_pos_invoices()
 
 		pos_inv.load_from_db()
 		rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
@@ -315,7 +315,7 @@
 
 	def test_merging_into_sales_invoice_with_discount_and_inclusive_tax(self):
 		from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
-		from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices
+		from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import consolidate_pos_invoices
 
 		frappe.db.sql("delete from `tabPOS Invoice`")
 		test_user, pos_profile = init_user_and_profile()
@@ -348,7 +348,7 @@
 		})
 		pos_inv2.submit()
 
-		merge_pos_invoices()
+		consolidate_pos_invoices()
 
 		pos_inv.load_from_db()
 		rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
@@ -357,7 +357,7 @@
 
 	def test_merging_with_validate_selling_price(self):
 		from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
-		from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices
+		from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import consolidate_pos_invoices
 
 		if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
 			frappe.db.set_value("Selling Settings", "Selling Settings", "validate_selling_price", 1)
@@ -393,7 +393,7 @@
 		})
 		pos_inv2.submit()
 
-		merge_pos_invoices()
+		consolidate_pos_invoices()
 
 		pos_inv2.load_from_db()
 		rounded_total = frappe.db.get_value("Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total")
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json
index 8f97639..da2984f 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json
@@ -7,6 +7,8 @@
  "field_order": [
   "posting_date",
   "customer",
+  "column_break_3",
+  "pos_closing_entry",
   "section_break_3",
   "pos_invoices",
   "references_section",
@@ -76,11 +78,22 @@
    "label": "Consolidated Credit Note",
    "options": "Sales Invoice",
    "read_only": 1
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "pos_closing_entry",
+   "fieldtype": "Link",
+   "label": "POS Closing Entry",
+   "options": "POS Closing Entry"
   }
  ],
+ "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-05-29 15:08:41.317100",
+ "modified": "2020-12-01 11:53:57.267579",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Invoice Merge Log",
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
index add27e9..58409cd 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
@@ -5,10 +5,13 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
-from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate
-from frappe.model.document import Document
-from frappe.model.mapper import map_doc
 from frappe.model import default_fields
+from frappe.model.document import Document
+from frappe.utils import flt, getdate, nowdate
+from frappe.utils.background_jobs import enqueue
+from frappe.model.mapper import map_doc, map_child_doc
+from frappe.utils.scheduler import is_scheduler_inactive
+from frappe.core.page.background_jobs.background_jobs import get_info
 
 from six import iteritems
 
@@ -61,7 +64,13 @@
 
 		self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
 
-		self.update_pos_invoices(sales_invoice, credit_note)
+		self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note)
+
+	def on_cancel(self):
+		pos_invoice_docs = [frappe.get_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
+
+		self.update_pos_invoices(pos_invoice_docs)
+		self.cancel_linked_invoices()
 
 	def process_merging_into_sales_invoice(self, data):
 		sales_invoice = self.get_new_sales_invoice()
@@ -83,7 +92,7 @@
 
 		credit_note.is_consolidated = 1
 		# TODO: return could be against multiple sales invoice which could also have been consolidated?
-		credit_note.return_against = self.consolidated_invoice
+		# credit_note.return_against = self.consolidated_invoice
 		credit_note.save()
 		credit_note.submit()
 		self.consolidated_credit_note = credit_note.name
@@ -111,7 +120,9 @@
 						i.qty = i.qty + item.qty
 				if not found:
 					item.rate = item.net_rate
-					items.append(item)
+					item.price_list_rate = 0
+					si_item = map_child_doc(item, invoice, {"doctype": "Sales Invoice Item"})
+					items.append(si_item)
 			
 			for tax in doc.get('taxes'):
 				found = False
@@ -147,6 +158,8 @@
 		invoice.set('taxes', taxes)
 		invoice.additional_discount_percentage = 0
 		invoice.discount_amount = 0.0
+		invoice.taxes_and_charges = None
+		invoice.ignore_pricing_rule = 1
 
 		return invoice
 	
@@ -159,17 +172,21 @@
 
 		return sales_invoice
 	
-	def update_pos_invoices(self, sales_invoice, credit_note):
-		for d in self.pos_invoices:
-			doc = frappe.get_doc('POS Invoice', d.pos_invoice)
-			if not doc.is_return:
-				doc.update({'consolidated_invoice': sales_invoice})
-			else:
-				doc.update({'consolidated_invoice': credit_note})
+	def update_pos_invoices(self, invoice_docs, sales_invoice='', credit_note=''):
+		for doc in invoice_docs:
+			doc.load_from_db()
+			doc.update({ 'consolidated_invoice': None if self.docstatus==2 else (credit_note if doc.is_return else sales_invoice) })
 			doc.set_status(update=True)
 			doc.save()
 
-def get_all_invoices():
+	def cancel_linked_invoices(self):
+		for si_name in [self.consolidated_invoice, self.consolidated_credit_note]:
+			if not si_name: continue
+			si = frappe.get_doc('Sales Invoice', si_name)
+			si.flags.ignore_validate = True
+			si.cancel()
+
+def get_all_unconsolidated_invoices():
 	filters = {
 		'consolidated_invoice': [ 'in', [ '', None ]],
 		'status': ['not in', ['Consolidated']],
@@ -180,7 +197,7 @@
 	
 	return pos_invoices
 
-def get_invoices_customer_map(pos_invoices):
+def get_invoice_customer_map(pos_invoices):
 	# pos_invoice_customer_map = { 'Customer 1': [{}, {}, {}], 'Custoemr 2' : [{}] }
 	pos_invoice_customer_map = {}
 	for invoice in pos_invoices:
@@ -190,20 +207,82 @@
 	
 	return pos_invoice_customer_map
 
-def merge_pos_invoices(pos_invoices=[]):
-	if not pos_invoices:
-		pos_invoices = get_all_invoices()
-	
-	pos_invoice_map = get_invoices_customer_map(pos_invoices)
-	create_merge_logs(pos_invoice_map)
+def consolidate_pos_invoices(pos_invoices=[], closing_entry={}):
+	invoices = pos_invoices or closing_entry.get('pos_transactions') or get_all_unconsolidated_invoices()
+	invoice_by_customer = get_invoice_customer_map(invoices)
 
-def create_merge_logs(pos_invoice_customer_map):
-	for customer, invoices in iteritems(pos_invoice_customer_map):
+	if len(invoices) >= 5 and closing_entry:
+		enqueue_job(create_merge_logs, invoice_by_customer, closing_entry)
+		closing_entry.set_status(update=True, status='Queued')
+	else:
+		create_merge_logs(invoice_by_customer, closing_entry)
+
+def unconsolidate_pos_invoices(closing_entry):
+	merge_logs = frappe.get_all(
+		'POS Invoice Merge Log',
+		filters={ 'pos_closing_entry': closing_entry.name },
+		pluck='name'
+	)
+
+	if len(merge_logs) >= 5:
+		enqueue_job(cancel_merge_logs, merge_logs, closing_entry)
+		closing_entry.set_status(update=True, status='Queued')
+	else:
+		cancel_merge_logs(merge_logs, closing_entry)
+
+def create_merge_logs(invoice_by_customer, closing_entry={}):
+	for customer, invoices in iteritems(invoice_by_customer):
 		merge_log = frappe.new_doc('POS Invoice Merge Log')
 		merge_log.posting_date = getdate(nowdate())
 		merge_log.customer = customer
+		merge_log.pos_closing_entry = closing_entry.get('name', None)
 
 		merge_log.set('pos_invoices', invoices)
 		merge_log.save(ignore_permissions=True)
 		merge_log.submit()
+	
+	if closing_entry:
+		closing_entry.set_status(update=True, status='Submitted')
+		closing_entry.update_opening_entry()
 
+def cancel_merge_logs(merge_logs, closing_entry={}):
+	for log in merge_logs:
+		merge_log = frappe.get_doc('POS Invoice Merge Log', log)
+		merge_log.flags.ignore_permissions = True
+		merge_log.cancel()
+
+	if closing_entry:
+		closing_entry.set_status(update=True, status='Cancelled')
+		closing_entry.update_opening_entry(for_cancel=True)
+
+def enqueue_job(job, invoice_by_customer, closing_entry):
+	check_scheduler_status()
+
+	job_name = closing_entry.get("name")
+	if not job_already_enqueued(job_name):
+		enqueue(
+			job,
+			queue="long",
+			timeout=10000,
+			event="processing_merge_logs",
+			job_name=job_name,
+			closing_entry=closing_entry,
+			invoice_by_customer=invoice_by_customer,
+			now=frappe.conf.developer_mode or frappe.flags.in_test
+		)
+
+		if job == create_merge_logs:
+			msg = _('POS Invoices will be consolidated in a background process')
+		else:
+			msg = _('POS Invoices will be unconsolidated in a background process')
+
+		frappe.msgprint(msg, alert=1)
+
+def check_scheduler_status():
+	if is_scheduler_inactive() and not frappe.flags.in_test:
+		frappe.throw(_("Scheduler is inactive. Cannot enqueue job."), title=_("Scheduler Inactive"))
+
+def job_already_enqueued(job_name):
+	enqueued_jobs = [d.get("job_name") for d in get_info()]
+	if job_name in enqueued_jobs:
+		return True
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py
index 0f34272..db046c9 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py
@@ -7,7 +7,7 @@
 import unittest
 from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
 from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
-from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices
+from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import consolidate_pos_invoices
 from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
 
 class TestPOSInvoiceMergeLog(unittest.TestCase):
@@ -34,7 +34,7 @@
 		})
 		pos_inv3.submit()
 
-		merge_pos_invoices()
+		consolidate_pos_invoices()
 
 		pos_inv.load_from_db()
 		self.assertTrue(frappe.db.exists("Sales Invoice", pos_inv.consolidated_invoice))
@@ -79,7 +79,7 @@
 		pos_inv_cn.paid_amount = -300
 		pos_inv_cn.submit()
 
-		merge_pos_invoices()
+		consolidate_pos_invoices()
 
 		pos_inv.load_from_db()
 		self.assertTrue(frappe.db.exists("Sales Invoice", pos_inv.consolidated_invoice))
diff --git a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
index acac1c4..0023a84 100644
--- a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
+++ b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
@@ -6,7 +6,6 @@
 import frappe
 from frappe import _
 from frappe.utils import cint, get_link_to_form
-from frappe.model.document import Document
 from erpnext.controllers.status_updater import StatusUpdater
 
 class POSOpeningEntry(StatusUpdater):
@@ -21,15 +20,16 @@
 
 		if not cint(frappe.db.get_value("User", self.user, "enabled")):
 			frappe.throw(_("User {} is disabled. Please select valid user/cashier").format(self.user))
-	
+
 	def validate_payment_method_account(self):
 		invalid_modes = []
 		for d in self.balance_details:
-			account = frappe.db.get_value("Mode of Payment Account", 
-				{"parent": d.mode_of_payment, "company": self.company}, "default_account")
-			if not account:
-				invalid_modes.append(get_link_to_form("Mode of Payment", d.mode_of_payment))
-		
+			if d.mode_of_payment:
+				account = frappe.db.get_value("Mode of Payment Account",
+					{"parent": d.mode_of_payment, "company": self.company}, "default_account")
+				if not account:
+					invalid_modes.append(get_link_to_form("Mode of Payment", d.mode_of_payment))
+
 		if invalid_modes:
 			if invalid_modes == 1:
 				msg = _("Please set default Cash or Bank account in Mode of Payment {}")
diff --git a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry_list.js b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry_list.js
index 6c26ded..1ad3c91 100644
--- a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry_list.js
+++ b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry_list.js
@@ -5,7 +5,7 @@
 frappe.listview_settings['POS Opening Entry'] = {
 	get_indicator: function(doc) {
 		var status_color = {
-			"Draft": "grey",
+			"Draft": "red",
 			"Open": "orange",
 			"Closed": "green",
 			"Cancelled": "red"
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.js b/erpnext/accounts/doctype/pos_profile/pos_profile.js
index 7f4f755..efdeb1a 100755
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.js
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.js
@@ -57,6 +57,8 @@
 				}
 			};
 		});
+
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
 	},
 
 	refresh: function(frm) {
@@ -67,6 +69,7 @@
 
 	company: function(frm) {
 		frm.trigger("toggle_display_account_head");
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
 	},
 
 	toggle_display_account_head: function(frm) {
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json
index 750ed82..4b69f6e 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.json
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json
@@ -12,8 +12,6 @@
   "company",
   "country",
   "column_break_9",
-  "update_stock",
-  "ignore_pricing_rule",
   "warehouse",
   "campaign",
   "company_address",
@@ -25,8 +23,14 @@
   "hide_images",
   "hide_unavailable_items",
   "auto_add_item_to_cart",
-  "item_groups",
   "column_break_16",
+  "update_stock",
+  "ignore_pricing_rule",
+  "allow_rate_change",
+  "allow_discount_change",
+  "section_break_23",
+  "item_groups",
+  "column_break_25",
   "customer_groups",
   "section_break_16",
   "print_format",
@@ -309,6 +313,7 @@
    "default": "1",
    "fieldname": "update_stock",
    "fieldtype": "Check",
+   "hidden": 1,
    "label": "Update Stock",
    "read_only": 1
   },
@@ -329,13 +334,34 @@
    "fieldname": "auto_add_item_to_cart",
    "fieldtype": "Check",
    "label": "Automatically Add Filtered Item To Cart"
+  },
+  {
+   "default": "0",
+   "fieldname": "allow_rate_change",
+   "fieldtype": "Check",
+   "label": "Allow User to Edit Rate"
+  },
+  {
+   "default": "0",
+   "fieldname": "allow_discount_change",
+   "fieldtype": "Check",
+   "label": "Allow User to Edit Discount"
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "section_break_23",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "column_break_25",
+   "fieldtype": "Column Break"
   }
  ],
  "icon": "icon-cog",
  "idx": 1,
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-12-20 13:59:28.877572",
+ "modified": "2021-01-06 14:42:41.713864",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Profile",
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index af8d21d..f28cee7 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -56,6 +56,7 @@
 		self.assertEqual(details.get("discount_percentage"), 10)
 
 		prule = frappe.get_doc(test_record.copy())
+		prule.priority = 1
 		prule.applicable_for = "Customer"
 		prule.title = "_Test Pricing Rule for Customer"
 		self.assertRaises(MandatoryError, prule.insert)
@@ -261,6 +262,7 @@
 			"rate_or_discount": "Discount Percentage",
 			"rate": 0,
 			"discount_percentage": 17.5,
+			"priority": 1,
 			"company": "_Test Company"
 		}).insert()
 
@@ -557,6 +559,7 @@
 		"rate": args.rate or 0.0,
 		"margin_rate_or_amount": args.margin_rate_or_amount or 0.0,
 		"condition": args.condition or '',
+		"priority": 1,
 		"apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0
 	})
 
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index fb1fbe4..d163335 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -41,10 +41,11 @@
 	if not pricing_rules: return []
 
 	if apply_multiple_pricing_rules(pricing_rules):
-		pricing_rules = sorted_by_priority(pricing_rules)
+		pricing_rules = sorted_by_priority(pricing_rules, args, doc)
 		for pricing_rule in pricing_rules:
-			pricing_rule = filter_pricing_rules(args, pricing_rule, doc)
-			if pricing_rule:
+			if isinstance(pricing_rule, list):
+				rules.extend(pricing_rule)
+			else:
 				rules.append(pricing_rule)
 	else:
 		pricing_rule = filter_pricing_rules(args, pricing_rules, doc)
@@ -53,17 +54,22 @@
 
 	return rules
 
-def sorted_by_priority(pricing_rules):
+def sorted_by_priority(pricing_rules, args, doc=None):
 	# If more than one pricing rules, then sort by priority
 	pricing_rules_list = []
 	pricing_rule_dict = {}
-	for pricing_rule in pricing_rules:
-		if not pricing_rule.get("priority"): continue
 
-		pricing_rule_dict.setdefault(cint(pricing_rule.get("priority")), []).append(pricing_rule)
+	for pricing_rule in pricing_rules:
+		pricing_rule = filter_pricing_rules(args, pricing_rule, doc)
+		if pricing_rule:
+			if not pricing_rule.get('priority'):
+				pricing_rule['priority'] = 1
+
+			if pricing_rule.get('apply_multiple_pricing_rules'):
+				pricing_rule_dict.setdefault(cint(pricing_rule.get("priority")), []).append(pricing_rule)
 
 	for key in sorted(pricing_rule_dict):
-		pricing_rules_list.append(pricing_rule_dict.get(key))
+		pricing_rules_list.extend(pricing_rule_dict.get(key))
 
 	return pricing_rules_list or pricing_rules
 
@@ -144,9 +150,7 @@
 
 	if not apply_multiple_rule: return False
 
-	if (apply_multiple_rule
-		and len(apply_multiple_rule) == len(pricing_rules)):
-		return True
+	return True
 
 def _get_tree_conditions(args, parenttype, table, allow_blank=True):
 	field = frappe.scrub(parenttype)
@@ -264,18 +268,6 @@
 		if max_priority:
 			pricing_rules = list(filter(lambda x: cint(x.priority)==max_priority, pricing_rules))
 
-	# apply internal priority
-	all_fields = ["item_code", "item_group", "brand", "customer", "customer_group", "territory",
-		"supplier", "supplier_group", "campaign", "sales_partner", "variant_of"]
-
-	if len(pricing_rules) > 1:
-		for field_set in [["item_code", "variant_of", "item_group", "brand"],
-			["customer", "customer_group", "territory"], ["supplier", "supplier_group"]]:
-				remaining_fields = list(set(all_fields) - set(field_set))
-				if if_all_rules_same(pricing_rules, remaining_fields):
-					pricing_rules = apply_internal_priority(pricing_rules, field_set, args)
-					break
-
 	if pricing_rules and not isinstance(pricing_rules, list):
 		pricing_rules = list(pricing_rules)
 
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 7830cfd..06aa20b 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -26,6 +26,11 @@
 			};
 		});
 	},
+
+	company: function() {
+		erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
+	},
+
 	onload: function() {
 		this._super();
 
@@ -41,6 +46,8 @@
 		if (this.frm.doc.supplier && this.frm.doc.__islocal) {
 			this.frm.trigger('supplier');
 		}
+
+		erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
 	},
 
 	refresh: function(doc) {
@@ -268,8 +275,11 @@
 
 	supplier: function() {
 		var me = this;
-		if(this.frm.updating_party_details)
+
+		// Do not update if inter company reference is there as the details will already be updated
+		if(this.frm.updating_party_details || this.frm.doc.inter_company_invoice_reference)
 			return;
+
 		erpnext.utils.get_party_details(this.frm, "erpnext.accounts.party.get_party_details",
 			{
 				posting_date: this.frm.doc.posting_date,
@@ -498,7 +508,7 @@
 frappe.ui.form.on("Purchase Invoice", {
 	setup: function(frm) {
 		frm.custom_make_buttons = {
-			'Purchase Invoice': 'Debit Note',
+			'Purchase Invoice': 'Return / Debit Note',
 			'Payment Entry': 'Payment'
 		}
 
@@ -511,15 +521,6 @@
 				}
 			}
 		}
-
-		frm.set_query("cost_center", function() {
-			return {
-				filters: {
-					company: frm.doc.company,
-					is_group: 0
-				}
-			};
-		});
 	},
 
 	onload: function(frm) {
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index c64ffd8..3ff0efe 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -57,8 +57,9 @@
   "set_warehouse",
   "rejected_warehouse",
   "col_break_warehouse",
-  "is_subcontracted",
+  "set_from_warehouse",
   "supplier_warehouse",
+  "is_subcontracted",
   "items_section",
   "update_stock",
   "scan_barcode",
@@ -515,6 +516,7 @@
   },
   {
    "depends_on": "update_stock",
+   "description": "Sets 'Accepted Warehouse' in each row of the items table.",
    "fieldname": "set_warehouse",
    "fieldtype": "Link",
    "label": "Set Accepted Warehouse",
@@ -544,17 +546,6 @@
    "print_hide": 1
   },
   {
-   "depends_on": "eval:doc.is_subcontracted==\"Yes\"",
-   "fieldname": "supplier_warehouse",
-   "fieldtype": "Link",
-   "label": "Supplier Warehouse",
-   "no_copy": 1,
-   "options": "Warehouse",
-   "print_hide": 1,
-   "print_width": "50px",
-   "width": "50px"
-  },
-  {
    "fieldname": "items_section",
    "fieldtype": "Section Break",
    "oldfieldtype": "Section Break",
@@ -1232,7 +1223,9 @@
    "fieldname": "inter_company_invoice_reference",
    "fieldtype": "Link",
    "label": "Inter Company Invoice Reference",
+   "no_copy": 1,
    "options": "Sales Invoice",
+   "print_hide": 1,
    "read_only": 1
   },
   {
@@ -1356,13 +1349,36 @@
    "fieldtype": "Link",
    "label": "Represents Company",
    "options": "Company"
+  },
+  {
+   "depends_on": "eval:doc.update_stock && doc.is_internal_supplier",
+   "description": "Sets 'From Warehouse' in each row of the items table.",
+   "fieldname": "set_from_warehouse",
+   "fieldtype": "Link",
+   "label": "Set From Warehouse",
+   "no_copy": 1,
+   "options": "Warehouse",
+   "print_hide": 1,
+   "print_width": "50px",
+   "width": "50px"
+  },
+  {
+   "depends_on": "eval:doc.update_stock && doc.is_subcontracted==\"Yes\"",
+   "fieldname": "supplier_warehouse",
+   "fieldtype": "Link",
+   "label": "Supplier Warehouse",
+   "no_copy": 1,
+   "options": "Warehouse",
+   "print_hide": 1,
+   "print_width": "50px",
+   "width": "50px"
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 204,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-12-11 12:46:12.796378",
+ "modified": "2021-03-09 21:56:28.748582",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index b52678e..dacd50a 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -443,7 +443,7 @@
 		else:
 			self.stock_received_but_not_billed = None
 			self.expenses_included_in_valuation = None
-		
+
 		self.negative_expense_to_be_booked = 0.0
 		gl_entries = []
 
@@ -457,7 +457,7 @@
 		self.make_internal_transfer_gl_entries(gl_entries)
 
 		gl_entries = make_regional_gl_entries(gl_entries, self)
-		
+
 		gl_entries = merge_similar_entries(gl_entries)
 
 		self.make_payment_gl_entries(gl_entries)
@@ -480,7 +480,7 @@
 		grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
 
 		if grand_total and not self.is_internal_transfer():
-				# Didnot use base_grand_total to book rounding loss gle
+				# Did not use base_grand_total to book rounding loss gle
 				grand_total_in_company_currency = flt(grand_total * self.conversion_rate,
 					self.precision("grand_total"))
 				gl_entries.append(
@@ -511,8 +511,8 @@
 		voucher_wise_stock_value = {}
 		if self.update_stock:
 			for d in frappe.get_all('Stock Ledger Entry',
-				fields = ["voucher_detail_no", "stock_value_difference"], filters={'voucher_no': self.name}):
-				voucher_wise_stock_value.setdefault(d.voucher_detail_no, d.stock_value_difference)
+				fields = ["voucher_detail_no", "stock_value_difference", "warehouse"], filters={'voucher_no': self.name}):
+				voucher_wise_stock_value.setdefault((d.voucher_detail_no, d.warehouse), d.stock_value_difference)
 
 		valuation_tax_accounts = [d.account_head for d in self.get("taxes")
 			if d.category in ('Valuation', 'Total and Valuation')
@@ -563,16 +563,17 @@
 							)
 
 					else:
-						gl_entries.append(
-							self.get_gl_dict({
-								"account": item.expense_account,
-								"against": self.supplier,
-								"debit": warehouse_debit_amount,
-								"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-								"cost_center": item.cost_center,
-								"project": item.project or self.project
-							}, account_currency, item=item)
-						)
+						if not self.is_internal_transfer():
+							gl_entries.append(
+								self.get_gl_dict({
+									"account": item.expense_account,
+									"against": self.supplier,
+									"debit": warehouse_debit_amount,
+									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+									"cost_center": item.cost_center,
+									"project": item.project or self.project
+								}, account_currency, item=item)
+							)
 
 					# Amount added through landed-cost-voucher
 					if landed_cost_entries:
@@ -582,7 +583,8 @@
 								"against": item.expense_account,
 								"cost_center": item.cost_center,
 								"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-								"credit": flt(amount),
+								"credit": flt(amount["base_amount"]),
+								"credit_in_account_currency": flt(amount["amount"]),
 								"project": item.project or self.project
 							}, item=item))
 
@@ -624,13 +626,14 @@
 							if expense_booked_in_pr:
 								expense_account = service_received_but_not_billed_account
 
-					gl_entries.append(self.get_gl_dict({
-							"account": expense_account,
-							"against": self.supplier,
-							"debit": amount,
-							"cost_center": item.cost_center,
-							"project": item.project or self.project
-						}, account_currency, item=item))
+					if not self.is_internal_transfer():
+						gl_entries.append(self.get_gl_dict({
+								"account": expense_account,
+								"against": self.supplier,
+								"debit": amount,
+								"cost_center": item.cost_center,
+								"project": item.project or self.project
+							}, account_currency, item=item))
 
 					# If asset is bought through this document and not linked to PR
 					if self.update_stock and item.landed_cost_voucher_amount:
@@ -795,10 +798,10 @@
 
 		# Stock ledger value is not matching with the warehouse amount
 		if (self.update_stock and voucher_wise_stock_value.get(item.name) and
-			warehouse_debit_amount != flt(voucher_wise_stock_value.get(item.name), net_amt_precision)):
+			warehouse_debit_amount != flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)):
 
 			cost_of_goods_sold_account = self.get_company_default("default_expense_account")
-			stock_amount = flt(voucher_wise_stock_value.get(item.name), net_amt_precision)
+			stock_amount = flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)
 			stock_adjustment_amt = warehouse_debit_amount - stock_amount
 
 			gl_entries.append(
@@ -999,10 +1002,10 @@
 			self.delete_auto_created_batches()
 
 		self.make_gl_entries_on_cancel()
-		
+
 		if self.update_stock == 1:
 			self.repost_future_sle_and_gle()
-		
+
 		self.update_project()
 		frappe.db.set(self, 'status', 'Cancelled')
 
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index c0506ba..ded293b 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -426,32 +426,37 @@
 		)
 
 	def test_total_purchase_cost_for_project(self):
-		make_project({'project_name':'_Test Project'})
+		if not frappe.db.exists("Project", {"project_name": "_Test Project for Purchase"}):
+			project = make_project({'project_name':'_Test Project for Purchase'})
+		else:
+			project = frappe.get_doc("Project", {"project_name": "_Test Project for Purchase"})
 
 		existing_purchase_cost = frappe.db.sql("""select sum(base_net_amount)
-			from `tabPurchase Invoice Item` where project = '_Test Project' and docstatus=1""")
+			from `tabPurchase Invoice Item`
+			where project = '{0}'
+			and docstatus=1""".format(project.name))
 		existing_purchase_cost = existing_purchase_cost and existing_purchase_cost[0][0] or 0
 
-		pi = make_purchase_invoice(currency="USD", conversion_rate=60, project="_Test Project")
-		self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"),
+		pi = make_purchase_invoice(currency="USD", conversion_rate=60, project=project.name)
+		self.assertEqual(frappe.db.get_value("Project", project.name, "total_purchase_cost"),
 			existing_purchase_cost + 15000)
 
-		pi1 = make_purchase_invoice(qty=10, project="_Test Project")
-		self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"),
+		pi1 = make_purchase_invoice(qty=10, project=project.name)
+		self.assertEqual(frappe.db.get_value("Project", project.name, "total_purchase_cost"),
 			existing_purchase_cost + 15500)
 
 		pi1.cancel()
-		self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"),
+		self.assertEqual(frappe.db.get_value("Project", project.name, "total_purchase_cost"),
 			existing_purchase_cost + 15000)
 
 		pi.cancel()
-		self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"), existing_purchase_cost)
+		self.assertEqual(frappe.db.get_value("Project", project.name, "total_purchase_cost"), existing_purchase_cost)
 
 	def test_return_purchase_invoice_with_perpetual_inventory(self):
 		pi = make_purchase_invoice(company = "_Test Company with perpetual inventory", warehouse= "Stores - TCP1",
 			cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1")
 
-		return_pi = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2, 
+		return_pi = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2,
 			company = "_Test Company with perpetual inventory", warehouse= "Stores - TCP1",
 			cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1")
 
@@ -860,17 +865,17 @@
 		})
 
 		pi = make_purchase_invoice(credit_to="Creditors - _TC" ,do_not_save=1)
-		pi.items[0].project = item_project.project_name
-		pi.project = project.project_name
+		pi.items[0].project = item_project.name
+		pi.project = project.name
 
 		pi.submit()
 
 		expected_values = {
 			"Creditors - _TC": {
-				"project": project.project_name
+				"project": project.name
 			},
 			"_Test Account Cost for Goods Sold - _TC": {
-				"project": item_project.project_name
+				"project": item_project.name
 			}
 		}
 
@@ -1026,7 +1031,7 @@
 	pi.is_return = args.is_return
 	pi.credit_to = args.return_against or "Creditors - _TC"
 	pi.is_subcontracted = args.is_subcontracted or "No"
-	if args.supplier_warehouse: 
+	if args.supplier_warehouse:
 		pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
 
 	pi.append("items", {
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index f6d76e5..07e75ac 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "autoname": "hash",
  "creation": "2013-05-22 12:43:10",
  "doctype": "DocType",
@@ -39,6 +40,7 @@
   "base_rate",
   "base_amount",
   "pricing_rules",
+  "stock_uom_rate",
   "is_free_item",
   "section_break_22",
   "net_rate",
@@ -87,6 +89,7 @@
   "po_detail",
   "purchase_receipt",
   "pr_detail",
+  "sales_invoice_item",
   "item_weight_details",
   "weight_per_unit",
   "total_weight",
@@ -553,8 +556,8 @@
    "fieldtype": "Link",
    "hidden": 1,
    "label": "Brand",
-   "print_hide": 1,
-   "options": "Brand"
+   "options": "Brand",
+   "print_hide": 1
   },
   {
    "fetch_from": "item_code.item_group",
@@ -562,9 +565,9 @@
    "fieldname": "item_group",
    "fieldtype": "Link",
    "label": "Item Group",
+   "options": "Item Group",
    "print_hide": 1,
-   "read_only": 1,
-   "options": "Item Group"
+   "read_only": 1
   },
   {
    "description": "Tax detail table fetched from item master as a string and stored in this field.\nUsed for Taxes and Charges",
@@ -759,10 +762,11 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:parent.is_internal_supplier && parent.update_stock",
    "fieldname": "from_warehouse",
    "fieldtype": "Link",
    "ignore_user_permissions": 1,
-   "label": "Supplier Warehouse",
+   "label": "From Warehouse",
    "options": "Warehouse"
   },
   {
@@ -779,11 +783,28 @@
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "depends_on": "eval: doc.uom != doc.stock_uom",
+   "fieldname": "stock_uom_rate",
+   "fieldtype": "Currency",
+   "label": "Rate of Stock UOM",
+   "options": "currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "sales_invoice_item",
+   "fieldtype": "Data",
+   "label": "Sales Invoice Item",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "idx": 1,
  "istable": 1,
- "modified": "2020-08-20 11:48:01.398356",
+ "links": [],
+ "modified": "2021-01-30 21:43:21.488258",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice Item",
@@ -791,4 +812,4 @@
  "permissions": [],
  "sort_field": "modified",
  "sort_order": "DESC"
-}
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 5efc32e..5bef9e2 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -5,18 +5,22 @@
 cur_frm.pformat.print_heading = 'Invoice';
 
 {% include 'erpnext/selling/sales_common.js' %};
-
-
 frappe.provide("erpnext.accounts");
+
+
 erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.extend({
 	setup: function(doc) {
 		this.setup_posting_date_time_check();
 		this._super(doc);
 	},
+	company: function() {
+		erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
+	},
 	onload: function() {
 		var me = this;
 		this._super();
 
+		this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice'];
 		if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
 			// show debit_to in print format
 			this.frm.set_df_property("debit_to", "print_hide", 0);
@@ -33,6 +37,7 @@
 			me.frm.refresh_fields();
 		}
 		erpnext.queries.setup_warehouse_query(this.frm);
+		erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
 	},
 
 	refresh: function(doc, dt, dn) {
@@ -126,16 +131,15 @@
 
 		this.set_default_print_format();
 		if (doc.docstatus == 1 && !doc.inter_company_invoice_reference) {
-			frappe.model.with_doc("Customer", me.frm.doc.customer, function() {
-				var customer = frappe.model.get_doc("Customer", me.frm.doc.customer);
-				var internal = customer.is_internal_customer;
-				var disabled = customer.disabled;
-				if (internal == 1 && disabled == 0) {
-					me.frm.add_custom_button("Inter Company Invoice", function() {
-						me.make_inter_company_invoice();
-					}, __('Create'));
-				}
-			});
+			let internal = me.frm.doc.is_internal_customer;
+			if (internal) {
+				let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Invoice" :
+					"Inter Company Purchase Invoice";
+
+				me.frm.add_custom_button(button_label, function() {
+					me.make_inter_company_invoice();
+				}, __('Create'));
+			}
 		}
 	},
 
@@ -571,15 +575,6 @@
 			};
 		});
 
-		frm.set_query("cost_center", function() {
-			return {
-				filters: {
-					company: frm.doc.company,
-					is_group: 0
-				}
-			};
-		});
-
 		frm.set_query("unrealized_profit_loss_account", function() {
 			return {
 				filters: {
@@ -592,7 +587,7 @@
 
 		frm.custom_make_buttons = {
 			'Delivery Note': 'Delivery',
-			'Sales Invoice': 'Sales Return',
+			'Sales Invoice': 'Return / Credit Note',
 			'Payment Request': 'Payment Request',
 			'Payment Entry': 'Payment'
 		},
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 6799fb9..018bc7e 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -60,6 +60,8 @@
   "ignore_pricing_rule",
   "sec_warehouse",
   "set_warehouse",
+  "column_break_55",
+  "set_target_warehouse",
   "items_section",
   "update_stock",
   "scan_barcode",
@@ -1969,13 +1971,31 @@
    "label": "Represents Company",
    "options": "Company",
    "read_only": 1
+  },
+  {
+   "fieldname": "column_break_55",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "eval: doc.is_internal_customer && doc.update_stock",
+   "fieldname": "set_target_warehouse",
+   "fieldtype": "Link",
+   "label": "Set Target Warehouse",
+   "options": "Warehouse"
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 181,
  "is_submittable": 1,
- "links": [],
- "modified": "2020-12-11 12:48:31.769958",
+ "links": [
+  {
+   "custom": 1,
+   "group": "Reference",
+   "link_doctype": "POS Invoice",
+   "link_fieldname": "consolidated_invoice"
+  }
+ ],
+ "modified": "2021-01-12 12:16:15.192520",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 40009ac..903a2ef 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -4,9 +4,9 @@
 from __future__ import unicode_literals
 import frappe, erpnext
 import frappe.defaults
-from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate, get_link_to_form
+from frappe.utils import cint, flt, getdate, add_days, cstr, nowdate, get_link_to_form, formatdate
 from frappe import _, msgprint, throw
-from erpnext.accounts.party import get_party_account, get_due_date
+from erpnext.accounts.party import get_party_account, get_due_date, get_party_details
 from frappe.model.mapper import get_mapped_doc
 from erpnext.controllers.selling_controller import SellingController
 from erpnext.accounts.utils import get_account_currency
@@ -21,6 +21,8 @@
 from erpnext.accounts.doctype.loyalty_program.loyalty_program import \
 	get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points
 from erpnext.accounts.deferred_revenue import validate_service_stop_date
+from frappe.model.utils import get_fetch_values
+from frappe.contacts.doctype.address.address import get_address_display
 
 from erpnext.healthcare.utils import manage_invoice_submit_cancel
 
@@ -179,7 +181,10 @@
 
 		# this sequence because outstanding may get -ve
 		self.make_gl_entries()
-		
+
+		if self.update_stock == 1:
+			self.repost_future_sle_and_gle()
+
 		if self.update_stock == 1:
 			self.repost_future_sle_and_gle()
 
@@ -231,7 +236,25 @@
 		if len(self.payments) == 0 and self.is_pos:
 			frappe.throw(_("At least one mode of payment is required for POS invoice."))
 
+	def check_if_consolidated_invoice(self):
+		# since POS Invoice extends Sales Invoice, we explicitly check if doctype is Sales Invoice
+		if self.doctype == "Sales Invoice" and self.is_consolidated:
+			invoice_or_credit_note = "consolidated_credit_note" if self.is_return else "consolidated_invoice"
+			pos_closing_entry = frappe.get_all(
+				"POS Invoice Merge Log",
+				filters={ invoice_or_credit_note: self.name },
+				pluck="pos_closing_entry"
+			)
+			if pos_closing_entry:
+				msg = _("To cancel a {} you need to cancel the POS Closing Entry {}. ").format(
+					frappe.bold("Consolidated Sales Invoice"),
+					get_link_to_form("POS Closing Entry", pos_closing_entry[0])
+				)
+				frappe.throw(msg, title=_("Not Allowed"))
+
 	def before_cancel(self):
+		self.check_if_consolidated_invoice()
+
 		super(SalesInvoice, self).before_cancel()
 		self.update_time_sheet(None)
 
@@ -261,10 +284,10 @@
 			self.update_stock_ledger()
 
 		self.make_gl_entries_on_cancel()
-		
+
 		if self.update_stock == 1:
 			self.repost_future_sle_and_gle()
-		
+
 		frappe.db.set(self, 'status', 'Cancelled')
 
 		if frappe.db.get_single_value('Selling Settings', 'sales_update_frequency') == "Each Transaction":
@@ -433,7 +456,9 @@
 			if not for_validate and not self.customer:
 				self.customer = pos.customer
 
-			self.ignore_pricing_rule = pos.ignore_pricing_rule
+			if not for_validate:
+				self.ignore_pricing_rule = pos.ignore_pricing_rule
+
 			if pos.get('account_for_change_amount'):
 				self.account_for_change_amount = pos.get('account_for_change_amount')
 
@@ -549,7 +574,12 @@
 		self.against_income_account = ','.join(against_acc)
 
 	def add_remarks(self):
-		if not self.remarks: self.remarks = 'No Remarks'
+		if not self.remarks:
+			if self.po_no and self.po_date:
+				self.remarks = _("Against Customer Order {0} dated {1}").format(self.po_no,
+					formatdate(self.po_date))
+			else:
+				self.remarks = _("No Remarks")
 
 	def validate_auto_set_posting_time(self):
 		# Don't auto set the posting date and time if invoice is amended
@@ -1529,7 +1559,7 @@
 	details = get_inter_company_details(doc, doctype)
 	price_list = doc.selling_price_list if doctype in ["Sales Invoice", "Sales Order", "Delivery Note"] else doc.buying_price_list
 	valid_price_list = frappe.db.get_value("Price List", {"name": price_list, "buying": 1, "selling": 1})
-	if not valid_price_list:
+	if not valid_price_list and not doc.is_internal_transfer():
 		frappe.throw(_("Selected Price List should have buying and selling fields checked."))
 
 	party = details.get("party")
@@ -1552,6 +1582,7 @@
 	if doctype in ["Sales Invoice", "Sales Order"]:
 		source_doc = frappe.get_doc(doctype, source_name)
 		target_doctype = "Purchase Invoice" if doctype == "Sales Invoice" else "Purchase Order"
+		target_detail_field = "sales_invoice_item" if doctype == "Sales Invoice" else "sales_order_item"
 		source_document_warehouse_field = 'target_warehouse'
 		target_document_warehouse_field = 'from_warehouse'
 	else:
@@ -1565,6 +1596,7 @@
 
 	def set_missing_values(source, target):
 		target.run_method("set_missing_values")
+		set_purchase_references(target)
 
 	def update_details(source_doc, target_doc, source_parent):
 		target_doc.inter_company_invoice_reference = source_doc.name
@@ -1572,19 +1604,38 @@
 			currency = frappe.db.get_value('Supplier', details.get('party'), 'default_currency')
 			target_doc.company = details.get("company")
 			target_doc.supplier = details.get("party")
+			target_doc.is_internal_supplier = 1
+			target_doc.ignore_pricing_rule = 1
 			target_doc.buying_price_list = source_doc.selling_price_list
 
+			# Invert Addresses
+			update_address(target_doc, 'supplier_address', 'address_display', source_doc.company_address)
+			update_address(target_doc, 'shipping_address', 'shipping_address_display', source_doc.customer_address)
+
 			if currency:
 				target_doc.currency = currency
+
+			update_taxes(target_doc, party=target_doc.supplier, party_type='Supplier', company=target_doc.company,
+				doctype=target_doc.doctype, party_address=target_doc.supplier_address,
+				company_address=target_doc.shipping_address)
+
 		else:
 			currency = frappe.db.get_value('Customer', details.get('party'), 'default_currency')
 			target_doc.company = details.get("company")
 			target_doc.customer = details.get("party")
 			target_doc.selling_price_list = source_doc.buying_price_list
 
+			update_address(target_doc, 'company_address', 'company_address_display', source_doc.supplier_address)
+			update_address(target_doc, 'shipping_address_name', 'shipping_address', source_doc.shipping_address)
+			update_address(target_doc, 'customer_address', 'address_display', source_doc.shipping_address)
+
 			if currency:
 				target_doc.currency = currency
 
+			update_taxes(target_doc, party=target_doc.customer, party_type='Customer', company=target_doc.company,
+				doctype=target_doc.doctype, party_address=target_doc.customer_address,
+				company_address=target_doc.company_address, shipping_address_name=target_doc.shipping_address_name)
+
 	item_field_map = {
 		"doctype": target_doctype + " Item",
 		"field_no_map": [
@@ -1592,25 +1643,33 @@
 			"expense_account",
 			"cost_center",
 			"warehouse"
-		]
+		],
+		"field_map": {
+			'rate': 'rate',
+		}
 	}
 
-	if source_doc.get('update_stock'):
-		item_field_map.update({
-			'field_map': {
-				source_document_warehouse_field: target_document_warehouse_field,
-				'batch_no': 'batch_no',
-				'serial_no': 'serial_no'
-			}
+	if doctype in ["Sales Invoice", "Sales Order"]:
+		item_field_map["field_map"].update({
+			"name": target_detail_field,
 		})
 
+	if source_doc.get('update_stock'):
+		item_field_map["field_map"].update({
+			source_document_warehouse_field: target_document_warehouse_field,
+			'batch_no': 'batch_no',
+			'serial_no': 'serial_no'
+		})
 
 	doclist = get_mapped_doc(doctype, source_name,	{
 		doctype: {
 			"doctype": target_doctype,
 			"postprocess": update_details,
+			"set_target_warehouse": "set_from_warehouse",
 			"field_no_map": [
-				"taxes_and_charges"
+				"taxes_and_charges",
+				"set_warehouse",
+				"shipping_address"
 			]
 		},
 		doctype +" Item": item_field_map
@@ -1619,6 +1678,110 @@
 
 	return doclist
 
+def set_purchase_references(doc):
+	# add internal PO or PR links if any
+	if doc.is_internal_transfer():
+		if doc.doctype == 'Purchase Receipt':
+			so_item_map = get_delivery_note_details(doc.inter_company_invoice_reference)
+
+			if so_item_map:
+				pd_item_map, parent_child_map, warehouse_map = \
+					get_pd_details('Purchase Order Item', so_item_map, 'sales_order_item')
+
+				update_pr_items(doc, so_item_map, pd_item_map, parent_child_map, warehouse_map)
+
+		elif doc.doctype == 'Purchase Invoice':
+			dn_item_map, so_item_map = get_sales_invoice_details(doc.inter_company_invoice_reference)
+			# First check for Purchase receipt
+			if list(dn_item_map.values()):
+				pd_item_map, parent_child_map, warehouse_map = \
+					get_pd_details('Purchase Receipt Item', dn_item_map, 'delivery_note_item')
+
+				update_pi_items(doc, 'pr_detail', 'purchase_receipt',
+					dn_item_map, pd_item_map, parent_child_map, warehouse_map)
+
+			if list(so_item_map.values()):
+				pd_item_map, parent_child_map, warehouse_map = \
+					get_pd_details('Purchase Order Item', so_item_map, 'sales_order_item')
+
+				update_pi_items(doc, 'po_detail', 'purchase_order',
+					so_item_map, pd_item_map, parent_child_map, warehouse_map)
+
+def update_pi_items(doc, detail_field, parent_field, sales_item_map,
+	purchase_item_map, parent_child_map, warehouse_map):
+	for item in doc.get('items'):
+		item.set(detail_field, purchase_item_map.get(sales_item_map.get(item.sales_invoice_item)))
+		item.set(parent_field, parent_child_map.get(sales_item_map.get(item.sales_invoice_item)))
+		if doc.update_stock:
+			item.warehouse = warehouse_map.get(sales_item_map.get(item.sales_invoice_item))
+
+def update_pr_items(doc, sales_item_map, purchase_item_map, parent_child_map, warehouse_map):
+	for item in doc.get('items'):
+		item.purchase_order_item = purchase_item_map.get(sales_item_map.get(item.delivery_note_item))
+		item.warehouse = warehouse_map.get(sales_item_map.get(item.delivery_note_item))
+		item.purchase_order = parent_child_map.get(sales_item_map.get(item.delivery_note_item))
+
+def get_delivery_note_details(internal_reference):
+	so_item_map = {}
+
+	si_item_details = frappe.get_all('Delivery Note Item', fields=['name', 'so_detail'],
+		filters={'parent': internal_reference})
+
+	for d in si_item_details:
+		so_item_map.setdefault(d.name, d.so_detail)
+
+	return so_item_map
+
+def get_sales_invoice_details(internal_reference):
+	dn_item_map = {}
+	so_item_map = {}
+
+	si_item_details = frappe.get_all('Sales Invoice Item', fields=['name', 'so_detail',
+		'dn_detail'], filters={'parent': internal_reference})
+
+	for d in si_item_details:
+		if d.dn_detail:
+			dn_item_map.setdefault(d.name, d.dn_detail)
+		if d.so_detail:
+			so_item_map.setdefault(d.name, d.so_detail)
+
+	return dn_item_map, so_item_map
+
+def get_pd_details(doctype, sd_detail_map, sd_detail_field):
+	pd_item_map = {}
+	accepted_warehouse_map = {}
+	parent_child_map = {}
+
+	pd_item_details = frappe.get_all(doctype,
+		fields=[sd_detail_field, 'name', 'warehouse', 'parent'], filters={sd_detail_field: ('in', list(sd_detail_map.values()))})
+
+	for d in pd_item_details:
+		pd_item_map.setdefault(d.get(sd_detail_field), d.name)
+		parent_child_map.setdefault(d.get(sd_detail_field), d.parent)
+		accepted_warehouse_map.setdefault(d.get(sd_detail_field), d.warehouse)
+
+	return pd_item_map, parent_child_map, accepted_warehouse_map
+
+def update_taxes(doc, party=None, party_type=None, company=None, doctype=None, party_address=None,
+	company_address=None, shipping_address_name=None, master_doctype=None):
+	# Update Party Details
+	party_details = get_party_details(party=party, party_type=party_type, company=company,
+		doctype=doctype, party_address=party_address, company_address=company_address,
+		shipping_address=shipping_address_name)
+
+	# Update taxes and charges if any
+	doc.taxes_and_charges = party_details.get('taxes_and_charges')
+	doc.set('taxes', party_details.get('taxes'))
+
+def update_address(doc, address_field, address_display_field, address_name):
+	doc.set(address_field, address_name)
+	fetch_values = get_fetch_values(doc.doctype, address_field, address_name)
+
+	for key, value in fetch_values.items():
+		doc.set(key, value)
+
+	doc.set(address_display_field, get_address_display(doc.get(address_field)))
+
 @frappe.whitelist()
 def get_loyalty_programs(customer):
 	''' sets applicable loyalty program to the customer or returns a list of applicable programs '''
@@ -1694,6 +1857,7 @@
 		where mpa.parent = mp.name and mpa.company = %s and mp.enabled = 1 and mp.name = %s""",
 	(company, mode_of_payment), as_dict=1)
 
+@frappe.whitelist()
 def create_dunning(source_name, target_doc=None):
 	from frappe.model.mapper import get_mapped_doc
 	from erpnext.accounts.doctype.dunning.dunning import get_dunning_letter_text, calculate_interest_and_amount
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 3c681ee..7cd1828 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -22,6 +22,7 @@
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
 from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+from erpnext.stock.utils import get_incoming_rate
 
 class TestSalesInvoice(unittest.TestCase):
 	def make(self):
@@ -688,7 +689,7 @@
 		self.assertTrue(gle)
 
 	def test_pos_gl_entry_with_perpetual_inventory(self):
-		make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1", 
+		make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
 			expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
 
 		pr = make_purchase_receipt(company= "_Test Company with perpetual inventory", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
@@ -745,7 +746,7 @@
 		self.assertEqual(pos_return.get('payments')[0].amount, -1000)
 
 	def test_pos_change_amount(self):
-		make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1", 
+		make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
 			expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
 
 		pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",
@@ -1573,17 +1574,17 @@
 		})
 
 		sales_invoice = create_sales_invoice(do_not_save=1)
-		sales_invoice.items[0].project = item_project.project_name
-		sales_invoice.project = project.project_name
+		sales_invoice.items[0].project = item_project.name
+		sales_invoice.project = project.name
 
 		sales_invoice.submit()
 
 		expected_values = {
 			"Debtors - _TC": {
-				"project": project.project_name
+				"project": project.name
 			},
 			"Sales - _TC": {
-				"project": item_project.project_name
+				"project": item_project.name
 			}
 		}
 
@@ -1770,59 +1771,82 @@
 		self.assertEqual(target_doc.company, "_Test Company 1")
 		self.assertEqual(target_doc.supplier, "_Test Internal Supplier")
 
-	# def test_internal_transfer_gl_entry(self):
-	# 	## Create internal transfer account
-	# 	account = create_account(account_name="Unrealized Profit",
-	# 		parent_account="Current Liabilities - TCP1", company="_Test Company with perpetual inventory")
+	def test_internal_transfer_gl_entry(self):
+		## Create internal transfer account
+		account = create_account(account_name="Unrealized Profit",
+			parent_account="Current Liabilities - TCP1", company="_Test Company with perpetual inventory")
 
-	# 	frappe.db.set_value('Company', '_Test Company with perpetual inventory',
-	# 		'unrealized_profit_loss_account', account)
+		frappe.db.set_value('Company', '_Test Company with perpetual inventory',
+			'unrealized_profit_loss_account', account)
 
-	# 	customer = create_internal_customer("_Test Internal Customer 2", "_Test Company with perpetual inventory",
-	# 		"_Test Company with perpetual inventory")
+		customer = create_internal_customer("_Test Internal Customer 2", "_Test Company with perpetual inventory",
+			"_Test Company with perpetual inventory")
 
-	# 	create_internal_supplier("_Test Internal Supplier 2", "_Test Company with perpetual inventory",
-	# 		"_Test Company with perpetual inventory")
+		create_internal_supplier("_Test Internal Supplier 2", "_Test Company with perpetual inventory",
+			"_Test Company with perpetual inventory")
 
-	# 	si = create_sales_invoice(
-	# 		company = "_Test Company with perpetual inventory",
-	# 		customer = customer,
-	# 		debit_to = "Debtors - TCP1",
-	# 		warehouse = "Stores - TCP1",
-	# 		income_account = "Sales - TCP1",
-	# 		expense_account = "Cost of Goods Sold - TCP1",
-	# 		cost_center = "Main - TCP1",
-	# 		currency = "INR",
-	# 		do_not_save = 1
-	# 	)
+		si = create_sales_invoice(
+			company = "_Test Company with perpetual inventory",
+			customer = customer,
+			debit_to = "Debtors - TCP1",
+			warehouse = "Stores - TCP1",
+			income_account = "Sales - TCP1",
+			expense_account = "Cost of Goods Sold - TCP1",
+			cost_center = "Main - TCP1",
+			currency = "INR",
+			do_not_save = 1
+		)
 
-	# 	si.selling_price_list = "_Test Price List Rest of the World"
-	# 	si.update_stock = 1
-	# 	si.items[0].target_warehouse = 'Work In Progress - TCP1'
-	# 	add_taxes(si)
-	# 	si.save()
-	# 	si.submit()
+		si.selling_price_list = "_Test Price List Rest of the World"
+		si.update_stock = 1
+		si.items[0].target_warehouse = 'Work In Progress - TCP1'
+		add_taxes(si)
+		si.save()
 
-	# 	target_doc = make_inter_company_transaction("Sales Invoice", si.name)
-	# 	target_doc.company = '_Test Company with perpetual inventory'
-	# 	target_doc.items[0].warehouse = 'Finished Goods - TCP1'
-	# 	add_taxes(target_doc)
-	# 	target_doc.save()
-	# 	target_doc.submit()
+		rate = 0.0
+		for d in si.get('items'):
+			rate = get_incoming_rate({
+				"item_code": d.item_code,
+				"warehouse": d.warehouse,
+				"posting_date": si.posting_date,
+				"posting_time": si.posting_time,
+				"qty": -1 * flt(d.get('stock_qty')),
+				"serial_no": d.serial_no,
+				"company": si.company,
+				"voucher_type": 'Sales Invoice',
+				"voucher_no": si.name,
+				"allow_zero_valuation": d.get("allow_zero_valuation")
+			}, raise_error_if_no_rate=False)
 
-	# 	si_gl_entries = [
-	# 		["_Test Account Excise Duty - TCP1", 0.0, 12.0, nowdate()],
-	# 		["Unrealized Profit - TCP1", 12.0, 0.0, nowdate()]
-	# 	]
+			rate = flt(rate, 2)
 
-	# 	check_gl_entries(self, si.name, si_gl_entries, add_days(nowdate(), -1))
+		si.submit()
 
-	# 	pi_gl_entries = [
-	# 		["_Test Account Excise Duty - TCP1", 12.0 , 0.0, nowdate()],
-	# 		["Unrealized Profit - TCP1", 0.0, 12.0, nowdate()]
-	# 	]
+		target_doc = make_inter_company_transaction("Sales Invoice", si.name)
+		target_doc.company = '_Test Company with perpetual inventory'
+		target_doc.items[0].warehouse = 'Finished Goods - TCP1'
+		add_taxes(target_doc)
+		target_doc.save()
+		target_doc.submit()
 
-	# 	check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1))
+		tax_amount = flt(rate * (12/100), 2)
+		si_gl_entries = [
+			["_Test Account Excise Duty - TCP1", 0.0, tax_amount, nowdate()],
+			["Unrealized Profit - TCP1", tax_amount, 0.0, nowdate()]
+		]
+
+		check_gl_entries(self, si.name, si_gl_entries, add_days(nowdate(), -1))
+
+		pi_gl_entries = [
+			["_Test Account Excise Duty - TCP1", tax_amount , 0.0, nowdate()],
+			["Unrealized Profit - TCP1", 0.0, tax_amount, nowdate()]
+		]
+
+		# Sale and Purchase both should be at valuation rate
+		self.assertEqual(si.items[0].rate, rate)
+		self.assertEqual(target_doc.items[0].rate, rate)
+
+		check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1))
 
 	def test_eway_bill_json(self):
 		si = make_sales_invoice_for_ewaybill()
@@ -1841,7 +1865,7 @@
 		self.assertEqual(data['billLists'][0]['sgstValue'], 5400)
 		self.assertEqual(data['billLists'][0]['vehicleNo'], 'KA12KA1234')
 		self.assertEqual(data['billLists'][0]['itemList'][0]['taxableAmount'], 60000)
-	
+
 	def test_einvoice_submission_without_irn(self):
 		# init
 		frappe.db.set_value('E Invoice Settings', 'E Invoice Settings', 'enable', 1)
@@ -1857,27 +1881,10 @@
 		# reset
 		frappe.db.set_value('E Invoice Settings', 'E Invoice Settings', 'enable', 0)
 		frappe.flags.country = country
-	
+
 	def test_einvoice_json(self):
 		from erpnext.regional.india.e_invoice.utils import make_einvoice
 
-		customer_gstin = '27AACCM7806M1Z3'
-		customer_gstin_dtls = {
-			'LegalName': '_Test Customer', 'TradeName': '_Test Customer', 'AddrLoc': '_Test City',
-			'StateCode': '27', 'AddrPncd': '410038', 'AddrBno': '_Test Bldg',
-			'AddrBnm': '100', 'AddrFlno': '200', 'AddrSt': '_Test Street'
-		}
-		company_gstin = '27AAECE4835E1ZR'
-		company_gstin_dtls = {
-			'LegalName': '_Test Company', 'TradeName': '_Test Company', 'AddrLoc': '_Test City',
-			'StateCode': '27', 'AddrPncd': '401108', 'AddrBno': '_Test Bldg',
-			'AddrBnm': '100', 'AddrFlno': '200', 'AddrSt': '_Test Street'
-		}
-		# set cache gstin details to avoid fetching details which will require connection to GSP servers
-		frappe.local.gstin_cache = {}
-		frappe.local.gstin_cache[customer_gstin] = customer_gstin_dtls
-		frappe.local.gstin_cache[company_gstin] = company_gstin_dtls
-
 		si = make_sales_invoice_for_ewaybill()
 		si.naming_series = 'INV-2020-.#####'
 		si.items = []
@@ -1885,8 +1892,8 @@
 			"item_code": "_Test Item",
 			"uom": "Nos",
 			"warehouse": "_Test Warehouse - _TC",
-			"qty": 2,
-			"rate": 100,
+			"qty": 2000,
+			"rate": 12,
 			"income_account": "Sales - _TC",
 			"expense_account": "Cost of Goods Sold - _TC",
 			"cost_center": "_Test Cost Center - _TC",
@@ -1895,31 +1902,52 @@
 			"item_code": "_Test Item 2",
 			"uom": "Nos",
 			"warehouse": "_Test Warehouse - _TC",
-			"qty": 4,
-			"rate": 150,
+			"qty": 420,
+			"rate": 15,
 			"income_account": "Sales - _TC",
 			"expense_account": "Cost of Goods Sold - _TC",
 			"cost_center": "_Test Cost Center - _TC",
 		})
+		si.discount_amount = 100
 		si.save()
 
 		einvoice = make_einvoice(si)
 
-		total_item_ass_value = sum([d['AssAmt'] for d in einvoice['ItemList']])
-		total_item_cgst_value = sum([d['CgstAmt'] for d in einvoice['ItemList']])
-		total_item_sgst_value = sum([d['SgstAmt'] for d in einvoice['ItemList']])
-		total_item_igst_value = sum([d['IgstAmt'] for d in einvoice['ItemList']])
-		total_item_value = sum([d['TotItemVal'] for d in einvoice['ItemList']])
+		total_item_ass_value = 0
+		total_item_cgst_value = 0
+		total_item_sgst_value = 0
+		total_item_igst_value = 0
+		total_item_value = 0
+
+		for item in einvoice['ItemList']:
+			total_item_ass_value += item['AssAmt']
+			total_item_cgst_value += item['CgstAmt']
+			total_item_sgst_value += item['SgstAmt']
+			total_item_igst_value += item['IgstAmt']
+			total_item_value += item['TotItemVal']
+
+			self.assertTrue(item['AssAmt'], item['TotAmt'] - item['Discount'])
+			self.assertTrue(item['TotItemVal'], item['AssAmt'] + item['CgstAmt'] + item['SgstAmt'] + item['IgstAmt'])
+
+		value_details = einvoice['ValDtls']
 
 		self.assertEqual(einvoice['Version'], '1.1')
-		self.assertEqual(einvoice['ValDtls']['AssVal'], total_item_ass_value)
-		self.assertEqual(einvoice['ValDtls']['CgstVal'], total_item_cgst_value)
-		self.assertEqual(einvoice['ValDtls']['SgstVal'], total_item_sgst_value)
-		self.assertEqual(einvoice['ValDtls']['IgstVal'], total_item_igst_value)
-		self.assertEqual(einvoice['ValDtls']['TotInvVal'], total_item_value)
+		self.assertEqual(value_details['AssVal'], total_item_ass_value)
+		self.assertEqual(value_details['CgstVal'], total_item_cgst_value)
+		self.assertEqual(value_details['SgstVal'], total_item_sgst_value)
+		self.assertEqual(value_details['IgstVal'], total_item_igst_value)
+
+		calculated_invoice_value = \
+			value_details['AssVal'] + value_details['CgstVal'] \
+			+ value_details['SgstVal'] + value_details['IgstVal'] \
+			+ value_details['OthChrg'] - value_details['Discount']
+
+		self.assertTrue(value_details['TotInvVal'] - calculated_invoice_value < 0.1)
+
+		self.assertEqual(value_details['TotInvVal'], si.base_grand_total)
 		self.assertTrue(einvoice['EwbDtls'])
 
-def make_sales_invoice_for_ewaybill():
+def make_test_address_for_ewaybill():
 	if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'):
 		address = frappe.get_doc({
 			"address_line1": "_Test Address Line 1",
@@ -1967,7 +1995,8 @@
 		})
 
 		address.save()
-	
+
+def make_test_transporter_for_ewaybill():
 	if not frappe.db.exists('Supplier', '_Test Transporter'):
 		frappe.get_doc({
 			"doctype": "Supplier",
@@ -1978,12 +2007,17 @@
 			"is_transporter": 1
 		}).insert()
 
+def make_sales_invoice_for_ewaybill():
+	make_test_address_for_ewaybill()
+	make_test_transporter_for_ewaybill()
+
 	gst_settings = frappe.get_doc("GST Settings")
 
 	gst_account = frappe.get_all(
 		"GST Account",
 		fields=["cgst_account", "sgst_account", "igst_account"],
-		filters = {"company": "_Test Company"})
+		filters = {"company": "_Test Company"}
+	)
 
 	if not gst_account:
 		gst_settings.append("gst_accounts", {
@@ -1995,7 +2029,7 @@
 
 	gst_settings.save()
 
-	si = create_sales_invoice(do_not_save =1, rate = '60000')
+	si = create_sales_invoice(do_not_save=1, rate='60000')
 
 	si.distance = 2000
 	si.company_address = "_Test Address for Eway bill-Billing"
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
index 3695075..b403c7b 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -45,6 +45,7 @@
   "base_rate",
   "base_amount",
   "pricing_rules",
+  "stock_uom_rate",
   "is_free_item",
   "section_break_21",
   "net_rate",
@@ -565,11 +566,12 @@
    "print_hide": 1
   },
   {
+   "depends_on": "eval: parent.is_internal_customer && parent.update_stock",
    "fieldname": "target_warehouse",
    "fieldtype": "Link",
    "hidden": 1,
    "ignore_user_permissions": 1,
-   "label": "Customer Warehouse (Optional)",
+   "label": "Target Warehouse",
    "no_copy": 1,
    "options": "Warehouse",
    "print_hide": 1
@@ -810,12 +812,20 @@
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "depends_on": "eval: doc.uom != doc.stock_uom",
+   "fieldname": "stock_uom_rate",
+   "fieldtype": "Currency",
+   "label": "Rate of Stock UOM",
+   "options": "currency",
+   "read_only": 1
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-09-23 19:59:04.879322",
+ "modified": "2021-01-30 21:42:37.796771",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py
index b46de6c..429a9f3 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py
@@ -34,6 +34,9 @@
 
 	validate_disabled(doc)
 
+	# Validate with existing taxes and charges template for unique tax category
+	validate_for_tax_category(doc)
+
 	for tax in doc.get("taxes"):
 		validate_taxes_and_charges(tax)
 		validate_inclusive_tax(tax, doc)
@@ -41,3 +44,7 @@
 def validate_disabled(doc):
 	if doc.is_default and doc.disabled:
 		frappe.throw(_("Disabled template must not be default template"))
+
+def validate_for_tax_category(doc):
+	if frappe.db.exists(doc.doctype, {"company": doc.company, "tax_category": doc.tax_category, "disabled": 0}):
+		frappe.throw(_("A template with tax category {0} already exists. Only one template is allowed with each tax category").format(frappe.bold(doc.tax_category)))
diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.js b/erpnext/accounts/doctype/shipping_rule/shipping_rule.js
index d0904ee..8e4b806 100644
--- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.js
+++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.js
@@ -1,16 +1,18 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-frappe.ui.form.on('Shipping Rule', {
-	refresh: function(frm) {
-		frm.set_query("cost_center", function() {
-			return {
-				filters: {
-					company: frm.doc.company
-				}
-			}
-		})
+frappe.provide('erpnext.accounts.dimensions');
 
+frappe.ui.form.on('Shipping Rule', {
+	onload: function(frm) {
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
+	},
+
+	company: function(frm) {
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
+	},
+
+	refresh: function(frm) {
 		frm.set_query("account", function() {
 			return {
 				filters: {
diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py
index 552a5d4..e023b47 100644
--- a/erpnext/accounts/doctype/subscription/subscription.py
+++ b/erpnext/accounts/doctype/subscription/subscription.py
@@ -446,7 +446,7 @@
 		if not self.generate_invoice_at_period_start:
 			return False
 
-		if self.is_new_subscription():
+		if self.is_new_subscription() and getdate() >= getdate(self.current_invoice_start):
 			return True
 
 		# Check invoice dates and make sure it doesn't have outstanding invoices
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index 287c79f..b42c0c6 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -44,9 +44,9 @@
 		frappe.throw(_("You cannot create or cancel any accounting entries with in the closed Accounting Period {0}")
 			.format(frappe.bold(accounting_periods[0].name)), ClosedAccountingPeriod)
 
-def process_gl_map(gl_map, merge_entries=True):
+def process_gl_map(gl_map, merge_entries=True, precision=None):
 	if merge_entries:
-		gl_map = merge_similar_entries(gl_map)
+		gl_map = merge_similar_entries(gl_map, precision)
 	for entry in gl_map:
 		# toggle debit, credit if negative entry
 		if flt(entry.debit) < 0:
@@ -69,7 +69,7 @@
 
 	return gl_map
 
-def merge_similar_entries(gl_map):
+def merge_similar_entries(gl_map, precision=None):
 	merged_gl_map = []
 	accounting_dimensions = get_accounting_dimensions()
 	for entry in gl_map:
@@ -88,7 +88,9 @@
 
 	company = gl_map[0].company if gl_map else erpnext.get_default_company()
 	company_currency = erpnext.get_company_currency(company)
-	precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
+
+	if not precision:
+		precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
 
 	# filter zero debit and credit entries
 	merged_gl_map = filter(lambda x: flt(x.debit, precision)!=0 or flt(x.credit, precision)!=0, merged_gl_map)
@@ -132,8 +134,8 @@
 	gle.update(args)
 	gle.flags.ignore_permissions = 1
 	gle.flags.from_repost = from_repost
-	gle.insert()
-	gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost)
+	gle.flags.adv_adj = adv_adj
+	gle.flags.update_outstanding = update_outstanding or 'Yes'
 	gle.submit()
 
 	if not from_repost:
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index 64268b8..38b2284 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -39,7 +39,7 @@
 	party_details = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype))
 	party = party_details[party_type.lower()]
 
-	if not ignore_permissions and not frappe.has_permission(party_type, "read", party):
+	if not ignore_permissions and not (frappe.has_permission(party_type, "read", party) or frappe.has_permission(party_type, "select", party)):
 		frappe.throw(_("Not permitted for {0}").format(party), frappe.PermissionError)
 
 	party = frappe.get_doc(party_type, party)
diff --git a/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html b/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html
index 9827e00..8eef2ad 100644
--- a/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html
+++ b/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html
@@ -152,7 +152,7 @@
 					<td class="text-right">{{ frappe.utils.fmt_money(value_details.CesVal, None, "INR") }}</td>
 					<td class="text-right">{{ frappe.utils.fmt_money(0, None, "INR") }}</td>
 					<td class="text-right">{{ frappe.utils.fmt_money(value_details.Discount, None, "INR") }}</td>
-					<td class="text-right">{{ frappe.utils.fmt_money(0, None, "INR") }}</td>
+					<td class="text-right">{{ frappe.utils.fmt_money(value_details.OthChrg, None, "INR") }}</td>
 					<td class="text-right">{{ frappe.utils.fmt_money(value_details.RndOffAmt, None, "INR") }}</td>
 					<td class="text-right">{{ frappe.utils.fmt_money(value_details.TotInvVal, None, "INR") }}</td>
 				</tr>
diff --git a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py
index 16bef56..2162a02 100644
--- a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py
+++ b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py
@@ -47,21 +47,22 @@
 
 	for d in gl_entries:
 		asset_data = assets_details.get(d.against_voucher)
-		if not asset_data.get("accumulated_depreciation_amount"):
-			asset_data.accumulated_depreciation_amount = d.debit
-		else:
-			asset_data.accumulated_depreciation_amount += d.debit
+		if asset_data:
+			if not asset_data.get("accumulated_depreciation_amount"):
+				asset_data.accumulated_depreciation_amount = d.debit
+			else:
+				asset_data.accumulated_depreciation_amount += d.debit
 
-		row = frappe._dict(asset_data)
-		row.update({
-			"depreciation_amount": d.debit,
-			"depreciation_date": d.posting_date,
-			"amount_after_depreciation": (flt(row.gross_purchase_amount) -
-				flt(row.accumulated_depreciation_amount)),
-			"depreciation_entry": d.voucher_no
-		})
+			row = frappe._dict(asset_data)
+			row.update({
+				"depreciation_amount": d.debit,
+				"depreciation_date": d.posting_date,
+				"amount_after_depreciation": (flt(row.gross_purchase_amount) -
+					flt(row.accumulated_depreciation_amount)),
+				"depreciation_entry": d.voucher_no
+			})
 
-		data.append(row)
+			data.append(row)
 
 	return data
 
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
index d011689..0c4a422 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -222,7 +222,7 @@
 
 		set_gl_entries_by_account(start_date,
 			end_date, root.lft, root.rgt, filters,
-			gl_entries_by_account, accounts_by_name, ignore_closing_entries=False)
+			gl_entries_by_account, accounts_by_name, accounts, ignore_closing_entries=False)
 
 	calculate_values(accounts_by_name, gl_entries_by_account, companies, start_date, filters)
 	accumulate_values_into_parents(accounts, accounts_by_name, companies)
@@ -240,8 +240,7 @@
 def calculate_values(accounts_by_name, gl_entries_by_account, companies, start_date, filters):
 	for entries in gl_entries_by_account.values():
 		for entry in entries:
-			key = entry.account_number or entry.account_name
-			d = accounts_by_name.get(key)
+			d = accounts_by_name.get(entry.account_name)
 			if d:
 				for company in companies:
 					# check if posting date is within the period
@@ -256,7 +255,8 @@
 	"""accumulate children's values in parent accounts"""
 	for d in reversed(accounts):
 		if d.parent_account:
-			account = d.parent_account.split(' - ')[0].strip()
+			account = d.parent_account_name
+
 			if not accounts_by_name.get(account):
 				continue
 
@@ -267,16 +267,34 @@
 			accounts_by_name[account]["opening_balance"] = \
 				accounts_by_name[account].get("opening_balance", 0.0) + d.get("opening_balance", 0.0)
 
+
 def get_account_heads(root_type, companies, filters):
 	accounts = get_accounts(root_type, filters)
 
 	if not accounts:
 		return None, None
 
+	accounts = update_parent_account_names(accounts)
+
 	accounts, accounts_by_name, parent_children_map = filter_accounts(accounts)
 
 	return accounts, accounts_by_name
 
+def update_parent_account_names(accounts):
+	"""Update parent_account_name in accounts list.
+
+		parent_name is `name` of parent account which could have other prefix
+		of account_number and suffix of company abbr. This function adds key called
+		`parent_account_name` which does not have such prefix/suffix.
+	"""
+	name_to_account_map = { d.name : d.account_name for d in accounts }
+
+	for account in accounts:
+		if account.parent_account:
+			account["parent_account_name"] = name_to_account_map[account.parent_account]
+
+	return accounts
+
 def get_companies(filters):
 	companies = {}
 	all_companies = get_subsidiary_companies(filters.get('company'))
@@ -339,7 +357,7 @@
 	return data
 
 def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, gl_entries_by_account,
-	accounts_by_name, ignore_closing_entries=False):
+	accounts_by_name, accounts, ignore_closing_entries=False):
 	"""Returns a dict like { "account": [gl entries], ... }"""
 
 	company_lft, company_rgt = frappe.get_cached_value('Company',
@@ -381,16 +399,32 @@
 			convert_to_presentation_currency(gl_entries, currency_info, filters.get('company'))
 
 		for entry in gl_entries:
-			key = entry.account_number or entry.account_name
-			validate_entries(key, entry, accounts_by_name)
-			gl_entries_by_account.setdefault(key, []).append(entry)
+			account_name =  entry.account_name
+			validate_entries(account_name, entry, accounts_by_name, accounts)
+			gl_entries_by_account.setdefault(account_name, []).append(entry)
 
 	return gl_entries_by_account
 
-def validate_entries(key, entry, accounts_by_name):
+def get_account_details(account):
+	return frappe.get_cached_value('Account', account, ['name', 'report_type', 'root_type', 'company',
+		'is_group', 'account_name', 'account_number', 'parent_account', 'lft', 'rgt'], as_dict=1)
+
+def validate_entries(key, entry, accounts_by_name, accounts):
 	if key not in accounts_by_name:
-		field = "Account number" if entry.account_number else "Account name"
-		frappe.throw(_("{0} {1} is not present in the parent company").format(field, key))
+		args = get_account_details(entry.account)
+
+		if args.parent_account:
+			parent_args = get_account_details(args.parent_account)
+
+			args.update({
+				'lft': parent_args.lft + 1,
+				'rgt': parent_args.rgt - 1,
+				'root_type': parent_args.root_type,
+				'report_type': parent_args.report_type
+			})
+
+		accounts_by_name.setdefault(key, args)
+		accounts.append(args)
 
 def get_additional_conditions(from_date, ignore_closing_entries, filters):
 	additional_conditions = []
@@ -436,8 +470,7 @@
 	parent_children_map = {}
 	accounts_by_name = {}
 	for d in accounts:
-		key = d.account_number or d.account_name
-		accounts_by_name[key] = d
+		accounts_by_name[d.account_name] = d
 		parent_children_map.setdefault(d.parent_account or None, []).append(d)
 
 	filtered_accounts = []
diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
index a36e7f8..cb4d9b4 100644
--- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
+++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
@@ -49,12 +49,13 @@
 		elif d.po_detail:
 			purchase_receipt = ", ".join(po_pr_map.get(d.po_detail, []))
 
-		expense_account = d.expense_account or aii_account_map.get(d.company)
+		expense_account = d.unrealized_profit_loss_account or d.expense_account \
+			or aii_account_map.get(d.company)
 
 		row = {
 			'item_code': d.item_code,
-			'item_name': item_record.item_name,
-			'item_group': item_record.item_group,
+			'item_name': item_record.item_name if item_record else d.item_name,
+			'item_group': item_record.item_group if item_record else d.item_group,
 			'description': d.description,
 			'invoice': d.parent,
 			'posting_date': d.posting_date,
@@ -315,7 +316,9 @@
 			`tabPurchase Invoice Item`.`name`, `tabPurchase Invoice Item`.`parent`,
 			`tabPurchase Invoice`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company,
 			`tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total,
+			`tabPurchase Invoice`.unrealized_profit_loss_account,
 			`tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description,
+			`tabPurchase Invoice Item`.`item_name`, `tabPurchase Invoice Item`.`item_group`,
 			`tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`,
 			`tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`,
 			`tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`,
diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
index f54ceb0..998003a 100644
--- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
+++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
@@ -76,7 +76,7 @@
 			'company': d.company,
 			'sales_order': d.sales_order,
 			'delivery_note': d.delivery_note,
-			'income_account': d.income_account,
+			'income_account': d.unrealized_profit_loss_account or d.income_account,
 			'cost_center': d.cost_center,
 			'stock_qty': d.stock_qty,
 			'stock_uom': d.stock_uom
@@ -379,6 +379,7 @@
 		select
 			`tabSales Invoice Item`.name, `tabSales Invoice Item`.parent,
 			`tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to,
+			`tabSales Invoice`.unrealized_profit_loss_account,
 			`tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks,
 			`tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total,
 			`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description,
diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
index 57a1231..7195c7e 100644
--- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
+++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
@@ -59,23 +59,111 @@
 
 def get_columns(filters):
 	return [
-		_("Payment Document") + ":: 100",
-		_("Payment Entry") + ":Dynamic Link/"+_("Payment Document")+":140",
-		_("Party Type") + "::100",
-		_("Party") + ":Dynamic Link/Party Type:140",
-		_("Posting Date") + ":Date:100",
-		_("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == _("Outgoing") else ":Link/Sales Invoice:130"),
-		_("Invoice Posting Date") + ":Date:130",
-		_("Payment Due Date") + ":Date:130",
-		_("Debit") + ":Currency:120",
-		_("Credit") + ":Currency:120",
-		_("Remarks") + "::150",
-		_("Age") +":Int:40",
-		"0-30:Currency:100",
-		"30-60:Currency:100",
-		"60-90:Currency:100",
-		_("90-Above") + ":Currency:100",
-		_("Delay in payment (Days)") + "::150"
+		{
+			"fieldname": "payment_document",
+			"label": _("Payment Document Type"),
+			"fieldtype": "Data",
+			"width": 100
+		},
+		{
+			"fieldname": "payment_entry",
+			"label": _("Payment Document"),
+			"fieldtype": "Dynamic Link",
+			"options": "payment_document",
+			"width": 160
+		},
+		{
+			"fieldname": "party_type",
+			"label": _("Party Type"),
+			"fieldtype": "Data",
+			"width": 100
+		},
+		{
+			"fieldname": "party",
+			"label": _("Party"),
+			"fieldtype": "Dynamic Link",
+			"options": "party_type",
+			"width": 160
+		},
+		{
+			"fieldname": "posting_date",
+			"label": _("Posting Date"),
+			"fieldtype": "Date",
+			"width": 100
+		},
+		{
+			"fieldname": "invoice",
+			"label": _("Invoice"),
+			"fieldtype": "Link",
+			"options": "Purchase Invoice" if filters.get("payment_type") == _("Outgoing") else "Sales Invoice",
+			"width": 160
+		},
+		{
+			"fieldname": "invoice_posting_date",
+			"label": _("Invoice Posting Date"),
+			"fieldtype": "Date",
+			"width": 100
+		},
+		{
+			"fieldname": "due_date",
+			"label": _("Payment Due Date"),
+			"fieldtype": "Date",
+			"width": 100
+		},
+		{
+			"fieldname": "debit",
+			"label": _("Debit"),
+			"fieldtype": "Currency",
+			"width": 140
+		},
+		{
+			"fieldname": "credit",
+			"label": _("Credit"),
+			"fieldtype": "Currency",
+			"width": 140
+		},
+		{
+			"fieldname": "remarks",
+			"label": _("Remarks"),
+			"fieldtype": "Data",
+			"width": 200
+		},
+		{
+			"fieldname": "age",
+			"label": _("Age"),
+			"fieldtype": "Int",
+			"width": 50
+		},
+		{
+			"fieldname": "range1",
+			"label": "0-30",
+			"fieldtype": "Currency",
+			"width": 140
+		},
+		{
+			"fieldname": "range2",
+			"label": "30-60",
+			"fieldtype": "Currency",
+			"width": 140
+		},
+		{
+			"fieldname": "range3",
+			"label": "60-90",
+			"fieldtype": "Currency",
+			"width": 140
+		},
+		{
+			"fieldname": "range4",
+			"label": _("90 Above"),
+			"fieldtype": "Currency",
+			"width": 140
+		},
+			{
+			"fieldname": "delay_in_payment",
+			"label": _("Delay in payment (Days)"),
+			"fieldtype": "Int",
+			"width": 100
+		}
 	]
 
 def get_conditions(filters):
diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py
index 9399e70..8ac749d 100644
--- a/erpnext/accounts/report/purchase_register/purchase_register.py
+++ b/erpnext/accounts/report/purchase_register/purchase_register.py
@@ -14,13 +14,15 @@
 	if not filters: filters = {}
 
 	invoice_list = get_invoices(filters, additional_query_columns)
-	columns, expense_accounts, tax_accounts = get_columns(invoice_list, additional_table_columns)
+	columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts \
+		= get_columns(invoice_list, additional_table_columns)
 
 	if not invoice_list:
 		msgprint(_("No record found"))
 		return columns, invoice_list
 
 	invoice_expense_map = get_invoice_expense_map(invoice_list)
+	internal_invoice_map = get_internal_invoice_map(invoice_list)
 	invoice_expense_map, invoice_tax_map = get_invoice_tax_map(invoice_list,
 		invoice_expense_map, expense_accounts)
 	invoice_po_pr_map = get_invoice_po_pr_map(invoice_list)
@@ -52,10 +54,17 @@
 		# map expense values
 		base_net_total = 0
 		for expense_acc in expense_accounts:
-			expense_amount = flt(invoice_expense_map.get(inv.name, {}).get(expense_acc))
+			if inv.is_internal_supplier and inv.company == inv.represents_company:
+				expense_amount = 0
+			else:
+				expense_amount = flt(invoice_expense_map.get(inv.name, {}).get(expense_acc))
 			base_net_total += expense_amount
 			row.append(expense_amount)
 
+		# Add amount in unrealized account
+		for account in unrealized_profit_loss_accounts:
+			row.append(flt(internal_invoice_map.get((inv.name, account))))
+
 		# net total
 		row.append(base_net_total or inv.base_net_total)
 
@@ -96,7 +105,8 @@
 			"width": 80
 		}
 	]
-	expense_accounts = tax_accounts = expense_columns = tax_columns = []
+	expense_accounts = tax_accounts = expense_columns = tax_columns = unrealized_profit_loss_accounts = \
+		unrealized_profit_loss_account_columns = []
 
 	if invoice_list:
 		expense_accounts = frappe.db.sql_list("""select distinct expense_account
@@ -112,17 +122,25 @@
 			and parent in (%s) order by account_head""" %
 			', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
 
+		unrealized_profit_loss_accounts = frappe.db.sql_list("""SELECT distinct unrealized_profit_loss_account
+			from `tabPurchase Invoice` where docstatus = 1 and name in (%s)
+			and ifnull(unrealized_profit_loss_account, '') != ''
+			order by unrealized_profit_loss_account""" %
+			', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
 
 	expense_columns = [(account + ":Currency/currency:120") for account in expense_accounts]
+	unrealized_profit_loss_account_columns = [(account + ":Currency/currency:120") for account in unrealized_profit_loss_accounts]
+
 	for account in tax_accounts:
 		if account not in expense_accounts:
 			tax_columns.append(account + ":Currency/currency:120")
 
-	columns = columns + expense_columns + [_("Net Total") + ":Currency/currency:120"] + tax_columns + \
+	columns = columns + expense_columns + unrealized_profit_loss_account_columns + \
+		[_("Net Total") + ":Currency/currency:120"] + tax_columns + \
 		[_("Total Tax") + ":Currency/currency:120", _("Grand Total") + ":Currency/currency:120",
 			_("Rounded Total") + ":Currency/currency:120", _("Outstanding Amount") + ":Currency/currency:120"]
 
-	return columns, expense_accounts, tax_accounts
+	return columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts
 
 def get_conditions(filters):
 	conditions = ""
@@ -199,6 +217,19 @@
 
 	return invoice_expense_map
 
+def get_internal_invoice_map(invoice_list):
+	unrealized_amount_details = frappe.db.sql("""SELECT name, unrealized_profit_loss_account,
+		base_net_total as amount from `tabPurchase Invoice` where name in (%s)
+		and is_internal_supplier = 1 and company = represents_company""" %
+		', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
+
+	internal_invoice_map = {}
+	for d in unrealized_amount_details:
+		if d.unrealized_profit_loss_account:
+			internal_invoice_map.setdefault((d.name, d.unrealized_profit_loss_account), d.amount)
+
+	return internal_invoice_map
+
 def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts):
 	tax_details = frappe.db.sql("""
 		select parent, account_head, case add_deduct_tax when "Add" then sum(base_tax_amount_after_discount_amount)
diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py
index b6e61b1..cb2c98b 100644
--- a/erpnext/accounts/report/sales_register/sales_register.py
+++ b/erpnext/accounts/report/sales_register/sales_register.py
@@ -15,13 +15,14 @@
 	if not filters: filters = frappe._dict({})
 
 	invoice_list = get_invoices(filters, additional_query_columns)
-	columns, income_accounts, tax_accounts = get_columns(invoice_list, additional_table_columns)
+	columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts = get_columns(invoice_list, additional_table_columns)
 
 	if not invoice_list:
 		msgprint(_("No record found"))
 		return columns, invoice_list
 
 	invoice_income_map = get_invoice_income_map(invoice_list)
+	internal_invoice_map = get_internal_invoice_map(invoice_list)
 	invoice_income_map, invoice_tax_map = get_invoice_tax_map(invoice_list,
 		invoice_income_map, income_accounts)
 	#Cost Center & Warehouse Map
@@ -70,12 +71,22 @@
 		# map income values
 		base_net_total = 0
 		for income_acc in income_accounts:
-			income_amount = flt(invoice_income_map.get(inv.name, {}).get(income_acc))
+			if inv.is_internal_customer and inv.company == inv.represents_company:
+				income_amount = 0
+			else:
+				income_amount = flt(invoice_income_map.get(inv.name, {}).get(income_acc))
+
 			base_net_total += income_amount
 			row.update({
 				frappe.scrub(income_acc): income_amount
 			})
 
+		# Add amount in unrealized account
+		for account in unrealized_profit_loss_accounts:
+			row.update({
+				frappe.scrub(account): flt(internal_invoice_map.get((inv.name, account)))
+			})
+
 		# net total
 		row.update({'net_total': base_net_total or inv.base_net_total})
 
@@ -230,6 +241,8 @@
 	tax_accounts = []
 	income_columns = []
 	tax_columns = []
+	unrealized_profit_loss_accounts = []
+	unrealized_profit_loss_account_columns = []
 
 	if invoice_list:
 		income_accounts = frappe.db.sql_list("""select distinct income_account
@@ -243,12 +256,18 @@
 			and parent in (%s) order by account_head""" %
 			', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
 
+		unrealized_profit_loss_accounts = frappe.db.sql_list("""SELECT distinct unrealized_profit_loss_account
+			from `tabSales Invoice` where docstatus = 1 and name in (%s)
+			and ifnull(unrealized_profit_loss_account, '') != ''
+			order by unrealized_profit_loss_account""" %
+			', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
+
 	for account in income_accounts:
 		income_columns.append({
 			"label": account,
 			"fieldname": frappe.scrub(account),
 			"fieldtype": "Currency",
-			"options": 'currency',
+			"options": "currency",
 			"width": 120
 		})
 
@@ -258,15 +277,24 @@
 				"label": account,
 				"fieldname": frappe.scrub(account),
 				"fieldtype": "Currency",
-				"options": 'currency',
+				"options": "currency",
 				"width": 120
 			})
 
+	for account in unrealized_profit_loss_accounts:
+		unrealized_profit_loss_account_columns.append({
+			"label": account,
+			"fieldname": frappe.scrub(account),
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 120
+		})
+
 	net_total_column = [{
 		"label": _("Net Total"),
 		"fieldname": "net_total",
 		"fieldtype": "Currency",
-		"options": 'currency',
+		"options": "currency",
 		"width": 120
 	}]
 
@@ -301,9 +329,10 @@
 		}
 	]
 
-	columns = columns + income_columns + net_total_column + tax_columns + total_columns
+	columns = columns + income_columns + unrealized_profit_loss_account_columns + \
+		net_total_column + tax_columns + total_columns
 
-	return columns, income_accounts, tax_accounts
+	return columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts
 
 def get_conditions(filters):
 	conditions = ""
@@ -368,7 +397,8 @@
 	return frappe.db.sql("""
 		select name, posting_date, debit_to, project, customer,
 		customer_name, owner, remarks, territory, tax_id, customer_group,
-		base_net_total, base_grand_total, base_rounded_total, outstanding_amount {0}
+		base_net_total, base_grand_total, base_rounded_total, outstanding_amount,
+		is_internal_customer, represents_company, company {0}
 		from `tabSales Invoice`
 		where docstatus = 1 %s order by posting_date desc, name desc""".format(additional_query_columns or '') %
 		conditions, filters, as_dict=1)
@@ -385,6 +415,19 @@
 
 	return invoice_income_map
 
+def get_internal_invoice_map(invoice_list):
+	unrealized_amount_details = frappe.db.sql("""SELECT name, unrealized_profit_loss_account,
+		base_net_total as amount from `tabSales Invoice` where name in (%s)
+		and is_internal_customer = 1 and company = represents_company""" %
+		', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
+
+	internal_invoice_map = {}
+	for d in unrealized_amount_details:
+		if d.unrealized_profit_loss_account:
+			internal_invoice_map.setdefault((d.name, d.unrealized_profit_loss_account), d.amount)
+
+	return internal_invoice_map
+
 def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts):
 	tax_details = frappe.db.sql("""select parent, account_head,
 		sum(base_tax_amount_after_discount_amount) as tax_amount
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 67c7fd2..89a05b1 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -82,7 +82,7 @@
 	error_msg = _("""{0} {1} is not in any active Fiscal Year""").format(label, formatdate(transaction_date))
 	if company:
 		error_msg = _("""{0} for {1}""").format(error_msg, frappe.bold(company))
-	
+
 	if verbose==1: frappe.msgprint(error_msg)
 	raise FiscalYearError(error_msg)
 
@@ -888,6 +888,11 @@
 
 def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
 		warehouse_account=None, company=None):
+	stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items, company)
+	repost_gle_for_stock_vouchers(stock_vouchers, posting_date, company, warehouse_account)
+
+
+def repost_gle_for_stock_vouchers(stock_vouchers, posting_date, company=None, warehouse_account=None):
 	def _delete_gl_entries(voucher_type, voucher_no):
 		frappe.db.sql("""delete from `tabGL Entry`
 			where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
@@ -895,21 +900,21 @@
 	if not warehouse_account:
 		warehouse_account = get_warehouse_account_map(company)
 
-	future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items)
-	gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date)
+	precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit")) or 2
 
-	for voucher_type, voucher_no in future_stock_vouchers:
+	gle = get_voucherwise_gl_entries(stock_vouchers, posting_date)
+	for voucher_type, voucher_no in stock_vouchers:
 		existing_gle = gle.get((voucher_type, voucher_no), [])
-		voucher_obj = frappe.get_doc(voucher_type, voucher_no)
+		voucher_obj = frappe.get_cached_doc(voucher_type, voucher_no)
 		expected_gle = voucher_obj.get_gl_entries(warehouse_account)
 		if expected_gle:
-			if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle):
+			if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle, precision):
 				_delete_gl_entries(voucher_type, voucher_no)
 				voucher_obj.make_gl_entries(gl_entries=expected_gle, from_repost=True)
 		else:
 			_delete_gl_entries(voucher_type, voucher_no)
 
-def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None):
+def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None, company=None):
 	future_stock_vouchers = []
 
 	values = []
@@ -922,6 +927,10 @@
 		condition += " and warehouse in ({})".format(", ".join(["%s"] * len(for_warehouses)))
 		values += for_warehouses
 
+	if company:
+		condition += " and company = %s"
+		values.append(company)
+
 	for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
 		from `tabStock Ledger Entry` sle
 		where
@@ -945,16 +954,17 @@
 
 	return gl_entries
 
-def compare_existing_and_expected_gle(existing_gle, expected_gle):
+def compare_existing_and_expected_gle(existing_gle, expected_gle, precision):
 	matched = True
 	for entry in expected_gle:
 		account_existed = False
 		for e in existing_gle:
 			if entry.account == e.account:
 				account_existed = True
-			if entry.account == e.account and entry.against_account == e.against_account \
-					and (not entry.cost_center or not e.cost_center or entry.cost_center == e.cost_center) \
-					and (entry.debit != e.debit or entry.credit != e.credit):
+			if (entry.account == e.account and entry.against_account == e.against_account
+					and (not entry.cost_center or not e.cost_center or entry.cost_center == e.cost_center)
+					and ( flt(entry.debit, precision) != flt(e.debit, precision) or
+						flt(entry.credit, precision) != flt(e.credit, precision))):
 				matched = False
 				break
 		if not account_existed:
@@ -982,7 +992,7 @@
 			error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses as on {3}.").format(
 				stock_bal, account_bal, frappe.bold(account), posting_date)
 			error_resolution = _("Please create an adjustment Journal Entry for amount {0} on {1}")\
-				.format(frappe.bold(diff), frappe.bold(posting_date))			
+				.format(frappe.bold(diff), frappe.bold(posting_date))
 
 			frappe.msgprint(
 				msg="""{0}<br></br>{1}<br></br>""".format(error_reason, error_resolution),
diff --git a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py
index cae150c..afbd9b4 100644
--- a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py
+++ b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py
@@ -48,7 +48,7 @@
 
 	def import_disease_tasks(self, disease, start_date):
 		disease_doc = frappe.get_doc('Disease', disease)
-		self.create_task(disease_doc.treatment_task, self.name, start_date)
+		self.create_task(disease_doc.treatment_task, self.project, start_date)
 
 	def create_project(self, period, crop_tasks):
 		project = frappe.get_doc({
diff --git a/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.py b/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.py
index 5510d5a..763b403 100644
--- a/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.py
+++ b/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.py
@@ -71,4 +71,4 @@
 
 
 def check_project_creation():
-	return True if frappe.db.exists('Project', 'Basil from seed 2017') else False
+	return True if frappe.db.exists('Project', {'project_name': 'Basil from seed 2017'}) else False
diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js
index b2318a2..6f1bb28 100644
--- a/erpnext/assets/doctype/asset/asset.js
+++ b/erpnext/assets/doctype/asset/asset.js
@@ -2,6 +2,7 @@
 // For license information, please see license.txt
 
 frappe.provide("erpnext.asset");
+frappe.provide("erpnext.accounts.dimensions");
 
 frappe.ui.form.on('Asset', {
 	onload: function(frm) {
@@ -32,13 +33,11 @@
 			};
 		});
 
-		frm.set_query("cost_center", function() {
-			return {
-				"filters": {
-					"company": frm.doc.company,
-				}
-			};
-		});
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
+	},
+
+	company: function(frm) {
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
 	},
 
 	setup: function(frm) {
diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js
index a6e6974..79c8861 100644
--- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js
+++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js
@@ -1,6 +1,8 @@
 // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 
+frappe.provide("erpnext.accounts.dimensions");
+
 frappe.ui.form.on('Asset Value Adjustment', {
 	setup: function(frm) {
 		frm.add_fetch('company', 'cost_center', 'cost_center');
@@ -13,11 +15,19 @@
 			}
 		});
 	},
+
 	onload: function(frm) {
 		if(frm.is_new() && frm.doc.asset) {
 			frm.trigger("set_current_asset_value");
 		}
+
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
 	},
+
+	company: function(frm) {
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
+	},
+
 	asset: function(frm) {
 		frm.trigger("set_current_asset_value");
 	},
diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
index c2579eb..1430827 100644
--- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
+++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
@@ -13,17 +13,14 @@
 class AssetValueAdjustment(Document):
 	def validate(self):
 		self.validate_date()
-		self.set_difference_amount()
 		self.set_current_asset_value()
+		self.set_difference_amount()
 
 	def on_submit(self):
 		self.make_depreciation_entry()
 		self.reschedule_depreciations(self.new_asset_value)
 
 	def on_cancel(self):
-		if self.journal_entry:
-			frappe.throw(_("Cancel the journal entry {0} first").format(self.journal_entry))
-
 		self.reschedule_depreciations(self.current_asset_value)
 
 	def validate_date(self):
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index 47483c9..dd0f065 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -2,7 +2,7 @@
 // License: GNU General Public License v3. See license.txt
 
 frappe.provide("erpnext.buying");
-
+frappe.provide("erpnext.accounts.dimensions");
 {% include 'erpnext/public/js/controllers/buying.js' %};
 
 frappe.ui.form.on("Purchase Order", {
@@ -30,6 +30,10 @@
 
 	},
 
+	company: function(frm) {
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
+	},
+
 	onload: function(frm) {
 		set_schedule_date(frm);
 		if (!frm.doc.transaction_date){
@@ -39,6 +43,8 @@
 		erpnext.queries.setup_queries(frm, "Warehouse", function() {
 			return erpnext.queries.warehouse(frm.doc);
 		});
+
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
 	}
 });
 
@@ -58,8 +64,8 @@
 erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({
 	setup: function() {
 		this.frm.custom_make_buttons = {
-			'Purchase Receipt': 'Receipt',
-			'Purchase Invoice': 'Invoice',
+			'Purchase Receipt': 'Purchase Receipt',
+			'Purchase Invoice': 'Purchase Invoice',
 			'Stock Entry': 'Material to Supplier',
 			'Payment Entry': 'Payment',
 		}
@@ -158,16 +164,16 @@
 
 					if (doc.docstatus === 1 && !doc.inter_company_order_reference) {
 						let me = this;
-						frappe.model.with_doc("Supplier", me.frm.doc.supplier, () => {
-							let supplier = frappe.model.get_doc("Supplier", me.frm.doc.supplier);
-							let internal = supplier.is_internal_supplier;
-							let disabled = supplier.disabled;
-							if (internal === 1 && disabled === 0) {
-								me.frm.add_custom_button("Inter Company Order", function() {
-									me.make_inter_company_order(me.frm);
-								}, __('Create'));
-							}
-						});
+						let internal = me.frm.doc.is_internal_supplier;
+						if (internal) {
+							let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Sales Order" :
+								"Inter Company Sales Order";
+
+							me.frm.add_custom_button(button_label, function() {
+								me.make_inter_company_order(me.frm);
+							}, __('Create'));
+						}
+
 					}
 				}
 
@@ -347,7 +353,8 @@
 	make_purchase_receipt: function() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_receipt",
-			frm: cur_frm
+			frm: cur_frm,
+			freeze_message: __("Creating Purchase Receipt ...")
 		})
 	},
 
@@ -374,7 +381,7 @@
 						material_request_type: "Purchase",
 						docstatus: 1,
 						status: ["!=", "Stopped"],
-						per_ordered: ["<", 99.99],
+						per_ordered: ["<", 100],
 						company: me.frm.doc.company
 					}
 				})
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index 75da71c..ee2beea 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -134,6 +134,8 @@
   "ref_sq",
   "column_break_74",
   "party_account_currency",
+  "is_internal_supplier",
+  "represents_company",
   "inter_company_order_reference"
  ],
  "fields": [
@@ -1101,13 +1103,28 @@
   {
    "fieldname": "items_col_break",
    "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "fetch_from": "supplier.is_internal_supplier",
+   "fieldname": "is_internal_supplier",
+   "fieldtype": "Check",
+   "label": "Is Internal Supplier"
+  },
+  {
+   "fetch_from": "supplier.represents_company",
+   "fieldname": "represents_company",
+   "fieldtype": "Link",
+   "label": "Represents Company",
+   "options": "Company",
+   "read_only": 1
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 105,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-12-03 16:46:44.229351",
+ "modified": "2021-01-20 22:07:23.487138",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order",
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index c7efb8a..d32e98e 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -123,8 +123,8 @@
 		if self.is_subcontracted == "Yes":
 			for item in self.items:
 				if not item.bom:
-					frappe.throw(_("BOM is not specified for subcontracting item {0} at row {1}"\
-						.format(item.item_code, item.idx)))
+					frappe.throw(_("BOM is not specified for subcontracting item {0} at row {1}")
+						.format(item.item_code, item.idx))
 
 	def get_schedule_dates(self):
 		for d in self.get('items'):
diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
index c691e9f..75b2954 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -40,6 +40,7 @@
   "base_rate",
   "base_amount",
   "pricing_rules",
+  "stock_uom_rate",
   "is_free_item",
   "section_break_29",
   "net_rate",
@@ -726,13 +727,21 @@
    "fieldname": "more_info_section_break",
    "fieldtype": "Section Break",
    "label": "More Information"
+  },
+  {
+   "depends_on": "eval: doc.uom != doc.stock_uom",
+   "fieldname": "stock_uom_rate",
+   "fieldtype": "Currency",
+   "label": "Rate of Stock UOM",
+   "options": "currency",
+   "read_only": 1
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-12-07 11:59:47.670951",
+ "modified": "2021-01-30 21:44:41.816974",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order Item",
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
index e537771..b76c378 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
@@ -224,7 +224,7 @@
 							material_request_type: "Purchase",
 							docstatus: 1,
 							status: ["!=", "Stopped"],
-							per_ordered: ["<", 99.99],
+							per_ordered: ["<", 100],
 							company: me.frm.doc.company
 						}
 					})
@@ -280,7 +280,7 @@
 								material_request_type: "Purchase",
 								docstatus: 1,
 								status: ["!=", "Stopped"],
-								per_ordered: ["<", 99.99]
+								per_ordered: ["<", 100]
 							}
 						});
 						dialog.hide();
diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py
index 0ee9d18..edeb135 100644
--- a/erpnext/buying/doctype/supplier/supplier.py
+++ b/erpnext/buying/doctype/supplier/supplier.py
@@ -52,7 +52,10 @@
 		self.validate_internal_supplier()
 
 	def validate_internal_supplier(self):
-		if self.is_internal_supplier and frappe.db.get_value("Supplier", {"represents_company": self.represents_company}, "name"):
+		internal_supplier = frappe.db.get_value("Supplier",
+			{"is_internal_supplier": 1, "represents_company": self.represents_company, "name": ("!=", self.name)}, "name")
+
+		if internal_supplier:
 			frappe.throw(_("Internal Supplier for company {0} already exists").format(
 				frappe.bold(self.represents_company)))
 
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
index a3b2085..a0187b0 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
@@ -44,7 +44,7 @@
 							material_request_type: "Purchase",
 							docstatus: 1,
 							status: ["!=", "Stopped"],
-							per_ordered: ["<", 99.99],
+							per_ordered: ["<", 100],
 							company: me.frm.doc.company
 						}
 					})
diff --git a/erpnext/buying/report/purchase_analytics/purchase_analytics.js b/erpnext/buying/report/purchase_analytics/purchase_analytics.js
index e17973c..ba8535a 100644
--- a/erpnext/buying/report/purchase_analytics/purchase_analytics.js
+++ b/erpnext/buying/report/purchase_analytics/purchase_analytics.js
@@ -75,62 +75,70 @@
 		return Object.assign(options, {
 			checkboxColumn: true,
 			events: {
-				onCheckRow: function(data) {
+				onCheckRow: function (data) {
+					if (!data) return;
+
+					const data_doctype = $(
+						data[2].html
+					)[0].attributes.getNamedItem("data-doctype").value;
+					const tree_type = frappe.query_report.filters[0].value;
+					if (data_doctype != tree_type) return;
+
 					row_name = data[2].content;
 					length = data.length;
 
-					var tree_type = frappe.query_report.filters[0].value;
-
-					if(tree_type == "Supplier" || tree_type == "Item") {
-						row_values = data.slice(4,length-1).map(function (column) {
-							return column.content;
-						})
-					}
-					else {
-						row_values = data.slice(3,length-1).map(function (column) {
-							return column.content;
-						})
+					if (tree_type == "Supplier") {
+						row_values = data
+							.slice(4, length - 1)
+							.map(function (column) {
+								return column.content;
+							});
+					} else if (tree_type == "Item") {
+						row_values = data
+							.slice(5, length - 1)
+							.map(function (column) {
+								return column.content;
+							});
+					} else {
+						row_values = data
+							.slice(3, length - 1)
+							.map(function (column) {
+								return column.content;
+							});
 					}
 
-					entry  = {
-						'name':row_name,
-						'values':row_values
-					}
+					entry = {
+						name: row_name,
+						values: row_values,
+					};
 
 					let raw_data = frappe.query_report.chart.data;
 					let new_datasets = raw_data.datasets;
 
-					var found = false;
-
-					for(var i=0; i < new_datasets.length;i++){
-						if(new_datasets[i].name == row_name){
-							found = true;
-							new_datasets.splice(i,1);
-							break;
+					let element_found = new_datasets.some((element, index, array)=>{
+						if(element.name == row_name){
+							array.splice(index, 1)
+							return true
 						}
-					}
+						return false
+					})
 
-					if(!found){
+					if (!element_found) {
 						new_datasets.push(entry);
 					}
-
 					let new_data = {
 						labels: raw_data.labels,
-						datasets: new_datasets
-					}
-
-					setTimeout(() => {
-						frappe.query_report.chart.update(new_data)
-					},500)
-
-
-					setTimeout(() => {
-						frappe.query_report.chart.draw(true);
-					}, 1000)
+						datasets: new_datasets,
+					};
+					chart_options = {
+						data: new_data,
+						type: "line",
+					};
+					frappe.query_report.render_chart(chart_options);
 
 					frappe.query_report.raw_chart_data = new_data;
 				},
-			}
+			},
 		});
 	}
 }
diff --git a/erpnext/buying/utils.py b/erpnext/buying/utils.py
index 47b4866..a73cb0d 100644
--- a/erpnext/buying/utils.py
+++ b/erpnext/buying/utils.py
@@ -35,9 +35,10 @@
 				frappe.throw(_("UOM Conversion factor is required in row {0}").format(d.idx))
 
 		# update last purchsae rate
-		if last_purchase_rate:
-			frappe.db.sql("""update `tabItem` set last_purchase_rate = %s where name = %s""",
-				(flt(last_purchase_rate), d.item_code))
+		frappe.db.set_value('Item', d.item_code, 'last_purchase_rate', flt(last_purchase_rate))
+
+
+
 
 def validate_for_items(doc):
 	items = []
diff --git a/erpnext/change_log/v13/v13_0_0-beta_11.md b/erpnext/change_log/v13/v13_0_0-beta_11.md
new file mode 100644
index 0000000..5c40ffb
--- /dev/null
+++ b/erpnext/change_log/v13/v13_0_0-beta_11.md
@@ -0,0 +1,77 @@
+### Version 13.0.0 Beta 11 Release Notes
+
+#### Features and Enhancements
+
+- Provision to disable serial no and batch selector ([#24398](https://github.com/frappe/erpnext/pull/24398))
+- Multi currency in landed cost voucher ([#24127](https://github.com/frappe/erpnext/pull/24127))
+- Putaway ([#23969](https://github.com/frappe/erpnext/pull/23969))
+- Item valuation for internal stock transfers ([#24200](https://github.com/frappe/erpnext/pull/24200))
+- Batch wise item pricing ([#24470](https://github.com/frappe/erpnext/pull/24470))
+- Project template with dependent tasks ([#24092](https://github.com/frappe/erpnext/pull/24092))
+- Patient History Enhancements ([#24033](https://github.com/frappe/erpnext/pull/24033))
+- Compute Year to Date for Salary Slip components ([#24362](https://github.com/frappe/erpnext/pull/24362))
+- Configurable accounting dimension filters and validations ([#23912](https://github.com/frappe/erpnext/pull/23912))
+- Issue Summary Script Report ([#23603](https://github.com/frappe/erpnext/pull/23603))
+- Issue Analytics Script Report ([#23604](https://github.com/frappe/erpnext/pull/23604))
+- Loan report and enhancements ([#24370](https://github.com/frappe/erpnext/pull/24370))
+- Enhancements to erpnext membership ([#23865](https://github.com/frappe/erpnext/pull/23865))
+- Allow Discharge despite Unbilled Healthcare Services ([#24281](https://github.com/frappe/erpnext/pull/24281))
+- Patient appointment status changes ([#24201](https://github.com/frappe/erpnext/pull/24201))
+- Allow selecting admission service unit in Patient Appointment for inpatients ([#24410](https://github.com/frappe/erpnext/pull/24410))
+- Separate equity tree in CoA SKR04 ([#24095](https://github.com/frappe/erpnext/pull/24095))
+- Do Not Bill Patient Encounters for Inpatients ([#24355](https://github.com/frappe/erpnext/pull/24355))
+- Value Based and Numeric Quality Inspection ([#24181](https://github.com/frappe/erpnext/pull/24181))
+- Deleting account & stock entries on deletion of transaction ([#24298](https://github.com/frappe/erpnext/pull/24298))
+- Remove german sales invoice validation ([#24441](https://github.com/frappe/erpnext/pull/24441))
+- Voice Call Settings doctype added ([#24126](https://github.com/frappe/erpnext/pull/24126))
+- Shopping portal changes ([#24445](https://github.com/frappe/erpnext/pull/24445))
+- Add "Sync Now" to Plaid Settings ([#23602](https://github.com/frappe/erpnext/pull/23602))
+
+#### Fixes
+
+- Multiple pricing rule with margin type as Percentage is not working ([#24204](https://github.com/frappe/erpnext/pull/24204))
+- Allow statistical component in salary structure. ([#24424](https://github.com/frappe/erpnext/pull/24424))
+- Set current asset value before calculating difference amount ([#24119](https://github.com/frappe/erpnext/pull/24119))
+- To use Stock UoM in BOM Stock Report ([#24339](https://github.com/frappe/erpnext/pull/24339))
+- Accounting entries of asset when submitting purchase receipt ([#24191](https://github.com/frappe/erpnext/pull/24191))
+- Cancelling of asset value adjustement ([#24193](https://github.com/frappe/erpnext/pull/24193))
+- Batch/Serial Selector for Scanned Batched Item ([#24338](https://github.com/frappe/erpnext/pull/24338))
+- Link timesheets with corresponding projects ([#24346](https://github.com/frappe/erpnext/pull/24346))
+- Material request wrong status issue ([#24019](https://github.com/frappe/erpnext/pull/24019))
+- UX issues in e-invoicing ([#24358](https://github.com/frappe/erpnext/pull/24358))
+- Company Wise Valuation Rate for RM in BOM ([#24324](https://github.com/frappe/erpnext/pull/24324))
+- Stock ageing should not take cancelled stock entries. ([#24437](https://github.com/frappe/erpnext/pull/24437))
+- Partial loan security unpledging ([#24252](https://github.com/frappe/erpnext/pull/24252))
+- Asset depreciation ledger ([#24226](https://github.com/frappe/erpnext/pull/24226))
+- Back Update from QC based on Batch No ([#24329](https://github.com/frappe/erpnext/pull/24329))
+- Fix for not having fiscal year while creating new company ([#24130](https://github.com/frappe/erpnext/pull/24130))
+- E-invoice print format not showing other charges ([#24474](https://github.com/frappe/erpnext/pull/24474))
+- Tax template update on customer address change ([#24146](https://github.com/frappe/erpnext/pull/24146))
+- Do not manufacture same serial no multiple times ([#24164](https://github.com/frappe/erpnext/pull/24164))
+- Ignore group cost center validation for period closing voucher ([#24375](https://github.com/frappe/erpnext/pull/24375))
+- Partial serial no return issue ([#24207](https://github.com/frappe/erpnext/pull/24207))
+- GSTR-1 double entry issue ([#24376](https://github.com/frappe/erpnext/pull/24376))
+- Not able to create dunning from sales invoice ([#24349](https://github.com/frappe/erpnext/pull/24349))
+- Set company in leave allocation and leave ledger entry ([#24296](https://github.com/frappe/erpnext/pull/24296))
+- Allow leave policy assignment to be canceled. ([#24265](https://github.com/frappe/erpnext/pull/24265))
+- Removed all day event from shift assignment calendar ([#24397](https://github.com/frappe/erpnext/pull/24397))
+- Tax calculation on salary slip for the first month ([#24272](https://github.com/frappe/erpnext/pull/24272))
+- Validate tax template for tax category ([#24402](https://github.com/frappe/erpnext/pull/24402))
+- Numeric/Non-numeric QI UX ([#24517](https://github.com/frappe/erpnext/pull/24517))
+- Finished good produced qty validation ([#24220](https://github.com/frappe/erpnext/pull/24220))
+- Incorrect serial no in the subcontracted purchase receipt ([#24354](https://github.com/frappe/erpnext/pull/24354))
+- Don't validate warehouse values between Material Request and Stock Entry ([#24294](https://github.com/frappe/erpnext/pull/24294))
+- Don't cancel job card if manufacturing entry has made ([#24063](https://github.com/frappe/erpnext/pull/24063))
+- Subscription prepaid date validation ([#24356](https://github.com/frappe/erpnext/pull/24356))
+- Allow addition and removal of employee in payroll Entry ([#24169](https://github.com/frappe/erpnext/pull/24169))
+- Filter Therapy Types and Therapy Plan in Patient Appointment ([#24152](https://github.com/frappe/erpnext/pull/24152))
+- Payment Period based on invoice date report fix/refactor ([#24378](https://github.com/frappe/erpnext/pull/24378))
+- Drop ship partial order fixed ([#24072](https://github.com/frappe/erpnext/pull/24072))
+- E-invoicing qrcode image generation ([#24395](https://github.com/frappe/erpnext/pull/24395))
+- Payment entry multi-currency issue ([#24332](https://github.com/frappe/erpnext/pull/24332))
+- Multiple pricing rule issue ([#24515](https://github.com/frappe/erpnext/pull/24515))
+- Last purchase rate not updating when voucher cancelled if only one voucher is present ([#24322](https://github.com/frappe/erpnext/pull/24322))
+- Do not cancel reference document on Quality Inspection cancellation ([#24197](https://github.com/frappe/erpnext/pull/24197))
+- Refactored fetching & validating address from erpnext rather than gst portal ([#24297](https://github.com/frappe/erpnext/pull/24297))
+- Opportunity Status fix ([#22944](https://github.com/frappe/erpnext/pull/22944))
+- Extra transferred qty has not consumed against work order ([#24495](https://github.com/frappe/erpnext/pull/24495))
\ No newline at end of file
diff --git a/erpnext/change_log/v13/v13_0_0-beta_12.md b/erpnext/change_log/v13/v13_0_0-beta_12.md
new file mode 100644
index 0000000..cb981f1
--- /dev/null
+++ b/erpnext/change_log/v13/v13_0_0-beta_12.md
@@ -0,0 +1,14 @@
+### Version 13.0.0 Beta 12 Release Notes
+#### Features
+- Department wise Appointment Type charges ([#24572](https://github.com/frappe/erpnext/pull/24572))
+- Capture Rate of stock UOM in purchase ([#24315](https://github.com/frappe/erpnext/pull/24315))
+
+#### Fixes
+
+- Fixed stock and account balance syncing ([#24644](https://github.com/frappe/erpnext/pull/24644))
+- Fixed incorrect stock ledger qty in the stock ledger report and bin ([#24649](https://github.com/frappe/erpnext/pull/24649))
+- Added patch to fix incorrect stock ledger and stock account value ([#24702](https://github.com/frappe/erpnext/pull/24702))
+- Skip e-invoice generation for non-taxable invoices ([#24568](https://github.com/frappe/erpnext/pull/24568))
+- Cannot cancel old invoices if eligible for e-invoicing ([#24608](https://github.com/frappe/erpnext/pull/24608))
+- Mpesa fixes and enhancement ([#24306](https://github.com/frappe/erpnext/pull/24306))
+- Fixed Consolidated Financial Statement report ([#24580](https://github.com/frappe/erpnext/pull/24580))
\ No newline at end of file
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 0f1aa23..e6b14c2 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -75,6 +75,9 @@
 		self.ensure_supplier_is_not_blocked()
 
 		self.validate_date_with_fiscal_year()
+		self.validate_inter_company_reference()
+
+		self.set_incoming_rate()
 
 		if self.meta.get_field("currency"):
 			self.calculate_taxes_and_totals()
@@ -110,15 +113,21 @@
 			self.set_inter_company_account()
 
 		validate_regional(self)
-		
+
 		validate_einvoice_fields(self)
 
 		if self.doctype != 'Material Request':
 			apply_pricing_rule_on_transaction(self)
-	
+
 	def before_cancel(self):
 		validate_einvoice_fields(self)
 
+	def on_trash(self):
+		# delete sl and gl entries on deletion of transaction
+		if frappe.db.get_single_value('Accounts Settings', 'delete_linked_ledger_entries'):
+			frappe.db.sql("delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s", (self.doctype, self.name))
+			frappe.db.sql("delete from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s", (self.doctype, self.name))
+
 	def validate_deferred_start_and_end_date(self):
 		for d in self.items:
 			if d.get("enable_deferred_revenue") or d.get("enable_deferred_expense"):
@@ -206,6 +215,17 @@
 				validate_fiscal_year(self.get(date_field), self.fiscal_year, self.company,
 									 self.meta.get_label(date_field), self)
 
+	def validate_inter_company_reference(self):
+		if self.doctype not in ('Purchase Invoice', 'Purchase Receipt', 'Purchase Order'):
+			return
+
+		if self.is_internal_transfer():
+			if not (self.get('inter_company_reference') or self.get('inter_company_invoice_reference')
+				or self.get('inter_company_order_reference')):
+				msg = _("Internal Sale or Delivery Reference missing. ")
+				msg += _("Please create purchase from internal sale or delivery document itself")
+				frappe.throw(msg, title=_("Internal Sales Reference Missing"))
+
 	def validate_due_date(self):
 		if self.get('is_pos'): return
 
@@ -282,6 +302,7 @@
 					args["doctype"] = self.doctype
 					args["name"] = self.name
 					args["child_docname"] = item.name
+					args["ignore_pricing_rule"] = self.ignore_pricing_rule if hasattr(self, 'ignore_pricing_rule') else 0
 
 					if not args.get("transaction_date"):
 						args["transaction_date"] = args.get("posting_date")
@@ -448,8 +469,10 @@
 			account_currency = get_account_currency(gl_dict.account)
 
 		if gl_dict.account and self.doctype not in ["Journal Entry",
-													"Period Closing Voucher", "Payment Entry"]:
+			"Period Closing Voucher", "Payment Entry", "Purchase Receipt", "Purchase Invoice", "Stock Entry"]:
 			self.validate_account_currency(gl_dict.account, account_currency)
+
+		if gl_dict.account and self.doctype not in ["Journal Entry", "Period Closing Voucher", "Payment Entry"]:
 			set_balance_in_account_currency(gl_dict, account_currency, self.get("conversion_rate"),
 											self.company_currency)
 
@@ -962,9 +985,9 @@
 			It will an internal transfer if its an internal customer and representation
 			company is same as billing company
 		"""
-		if self.doctype == 'Sales Invoice':
+		if self.doctype in ('Sales Invoice', 'Delivery Note', 'Sales Order'):
 			internal_party_field = 'is_internal_customer'
-		else:
+		elif self.doctype in ('Purchase Invoice', 'Purchase Receipt', 'Purchase Order'):
 			internal_party_field = 'is_internal_supplier'
 
 		if self.get(internal_party_field) and (self.represents_company == self.company):
@@ -1481,6 +1504,7 @@
 	parent.flags.ignore_validate_update_after_submit = True
 	parent.set_qty_as_per_stock_uom()
 	parent.calculate_taxes_and_totals()
+	parent.set_total_in_words()
 	if parent_doctype == "Sales Order":
 		make_packing_list(parent)
 		parent.set_gross_profit()
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 6edc020..305a162 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -44,7 +44,6 @@
 		self.validate_items()
 		self.set_qty_as_per_stock_uom()
 		self.validate_stock_or_nonstock_items()
-		self.update_tax_category_for_internal_transfer()
 		self.validate_warehouse()
 		self.validate_from_warehouse()
 		self.set_supplier_address()
@@ -100,11 +99,6 @@
 			msg = _('Tax Category has been changed to "Total" because all the Items are non-stock items')
 			self.update_tax_category(msg)
 
-	def update_tax_category_for_internal_transfer(self):
-		if self.doctype == 'Purchase Invoice' and self.is_internal_transfer():
-			msg = _('Tax Category has been changed to "Total" as its an internal purchase.')
-			self.update_tax_category(msg)
-
 	def update_tax_category(self, msg):
 		tax_for_valuation = [d for d in self.get("taxes")
 				if d.category in ["Valuation", "Valuation and Total"]]
@@ -224,6 +218,48 @@
 			else:
 				item.valuation_rate = 0.0
 
+	def set_incoming_rate(self):
+		if self.doctype not in ("Purchase Receipt", "Purchase Invoice", "Purchase Order"):
+			return
+
+		ref_doctype_map = {
+			"Purchase Order": "Sales Order Item",
+			"Purchase Receipt": "Delivery Note Item",
+			"Purchase Invoice": "Sales Invoice Item",
+		}
+
+		ref_doctype = ref_doctype_map.get(self.doctype)
+		items = self.get("items")
+		for d in items:
+			if not cint(self.get("is_return")):
+				# Get outgoing rate based on original item cost based on valuation method
+
+				if not d.get(frappe.scrub(ref_doctype)):
+					outgoing_rate = get_incoming_rate({
+						"item_code": d.item_code,
+						"warehouse": d.get('from_warehouse'),
+						"posting_date": self.get('posting_date') or self.get('transation_date'),
+						"posting_time": self.get('posting_time'),
+						"qty": -1 * flt(d.get('stock_qty')),
+						"serial_no": d.get('serial_no'),
+						"company": self.company,
+						"voucher_type": self.doctype,
+						"voucher_no": self.name,
+						"allow_zero_valuation": d.get("allow_zero_valuation")
+					}, raise_error_if_no_rate=False)
+
+					rate = flt(outgoing_rate * d.conversion_factor, d.precision('rate'))
+				else:
+					rate = frappe.db.get_value(ref_doctype, d.get(frappe.scrub(ref_doctype)), 'rate')
+
+				if self.is_internal_transfer():
+					if rate != d.rate:
+						d.rate = rate
+						d.discount_percentage = 0
+						d.discount_amount = 0
+						frappe.msgprint(_("Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer")
+							.format(d.idx), alert=1)
+
 	def get_supplied_items_cost(self, item_row_id, reset_outgoing_rate=True):
 		supplied_items_cost = 0.0
 		for d in self.get("supplied_items"):
@@ -243,7 +279,7 @@
 
 				d.amount = flt(flt(d.consumed_qty) * flt(d.rate), d.precision("amount"))
 				supplied_items_cost += flt(d.amount)
-		
+
 		return supplied_items_cost
 
 	def validate_for_subcontracting(self):
@@ -252,7 +288,7 @@
 
 		if self.is_subcontracted == "Yes":
 			if self.doctype in ["Purchase Receipt", "Purchase Invoice"] and not self.supplier_warehouse:
-				frappe.throw(_("Supplier Warehouse mandatory for sub-contracted Purchase Receipt"))
+				frappe.throw(_("Supplier Warehouse mandatory for sub-contracted {0}").format(self.doctype))
 
 			for item in self.get("items"):
 				if item in self.sub_contracted_items and not item.bom:
@@ -336,7 +372,7 @@
 				raw_material_data = backflushed_raw_materials_map.get(rm_item_key, {})
 
 				consumed_qty = raw_material_data.get('qty', 0)
-				consumed_serial_nos = raw_material_data.get('serial_nos', '')
+				consumed_serial_nos = raw_material_data.get('serial_no', '')
 				consumed_batch_nos = raw_material_data.get('batch_nos', '')
 
 				transferred_qty = raw_material.qty
@@ -559,6 +595,8 @@
 						from_warehouse_sle = self.get_sl_entries(d, {
 							"actual_qty": -1 * pr_qty,
 							"warehouse": d.from_warehouse,
+							"outgoing_rate": d.rate,
+							"recalculate_rate": 1,
 							"dependant_sle_voucher_detail_no": d.name
 						})
 
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 8fe3816..81f0ad3 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -493,6 +493,41 @@
 				'company': filters.get("company", "")
 			})
 
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
+def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters):
+	from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import get_dimension_filter_map
+	dimension_filters = get_dimension_filter_map()
+	dimension_filters = dimension_filters.get((filters.get('dimension'),filters.get('account')))
+	query_filters = []
+
+	meta = frappe.get_meta(doctype)
+	if meta.is_tree:
+		query_filters.append(['is_group', '=', 0])
+
+	if meta.has_field('company'):
+		query_filters.append(['company', '=', filters.get('company')])
+
+	if txt:
+		query_filters.append([searchfield, 'LIKE', "%%%s%%" % txt])
+
+	if dimension_filters:
+		if dimension_filters['allow_or_restrict'] == 'Allow':
+			query_selector = 'in'
+		else:
+			query_selector = 'not in'
+
+		if len(dimension_filters['allowed_dimensions']) == 1:
+			dimensions = tuple(dimension_filters['allowed_dimensions'] * 2)
+		else:
+			dimensions = tuple(dimension_filters['allowed_dimensions'])
+
+		query_filters.append(['name', query_selector, dimensions])
+
+	output = frappe.get_all(doctype, filters=query_filters)
+	result = [d.name for d in output]
+
+	return [(d,) for d in set(result)]
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
@@ -622,6 +657,34 @@
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
+def get_healthcare_service_units(doctype, txt, searchfield, start, page_len, filters):
+	query = """
+		select name
+		from `tabHealthcare Service Unit`
+		where
+			is_group = 0
+			and company = {company}
+			and name like {txt}""".format(
+				company = frappe.db.escape(filters.get('company')), txt = frappe.db.escape('%{0}%'.format(txt)))
+
+	if filters and filters.get('inpatient_record'):
+		from erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry import get_current_healthcare_service_unit
+		service_unit = get_current_healthcare_service_unit(filters.get('inpatient_record'))
+
+		# if the patient is admitted, then appointments should be allowed against the admission service unit,
+		# inspite of it being an Inpatient Occupancy service unit
+		if service_unit:
+			query += " and (allow_appointments = 1 or name = {service_unit})".format(service_unit = frappe.db.escape(service_unit))
+		else:
+			query += " and allow_appointments = 1"
+	else:
+		query += " and allow_appointments = 1"
+
+	return frappe.db.sql(query, filters)
+
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
 def get_tax_template(doctype, txt, searchfield, start, page_len, filters):
 
 	item_doc = frappe.get_cached_doc('Item', filters.get('item_code'))
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 7979226..0e1829a 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -262,6 +262,7 @@
 
 		if doc.get("is_return"):
 			if doc.doctype == 'Sales Invoice' or doc.doctype == 'POS Invoice':
+				doc.consolidated_invoice = ""
 				doc.set('payments', [])
 				for data in source.payments:
 					paid_amount = 0.00
@@ -328,6 +329,7 @@
 			target_doc.po_detail = source_doc.po_detail
 			target_doc.pr_detail = source_doc.pr_detail
 			target_doc.purchase_invoice_item = source_doc.name
+			target_doc.price_list_rate = 0
 
 		elif doctype == "Delivery Note":
 			returned_qty_map = get_returned_qty_map_for_row(source_doc.name, doctype)
@@ -353,6 +355,7 @@
 			target_doc.dn_detail = source_doc.dn_detail
 			target_doc.expense_account = source_doc.expense_account
 			target_doc.sales_invoice_item = source_doc.name
+			target_doc.price_list_rate = 0
 			if default_warehouse_for_sales_return:
 				target_doc.warehouse = default_warehouse_for_sales_return
 
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 85cfb95..3e82d46 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -3,7 +3,7 @@
 
 from __future__ import unicode_literals
 import frappe
-from frappe.utils import cint, flt, cstr, comma_or, get_link_to_form
+from frappe.utils import cint, flt, cstr, get_link_to_form, nowtime
 from frappe import _, throw
 from erpnext.stock.get_item_details import get_bin_details
 from erpnext.stock.utils import get_incoming_rate
@@ -49,7 +49,6 @@
 		self.set_customer_address()
 		self.validate_for_duplicate_items()
 		self.validate_target_warehouse()
-		self.set_incoming_rate()
 
 	def set_missing_values(self, for_validate=False):
 
@@ -191,7 +190,7 @@
 		for it in self.get("items"):
 			if not it.item_code:
 				continue
-			
+
 			last_purchase_rate, is_stock_item = frappe.get_cached_value("Item", it.item_code, ["last_purchase_rate", "is_stock_item"])
 			last_purchase_rate_in_sales_uom = last_purchase_rate * (it.conversion_factor or 1)
 			if flt(it.base_net_rate) < flt(last_purchase_rate_in_sales_uom):
@@ -233,7 +232,7 @@
 							'allow_zero_valuation': d.allow_zero_valuation_rate,
 							'sales_invoice_item': d.get("sales_invoice_item"),
 							'dn_detail': d.get("dn_detail"),
-							'incoming_rate': p.incoming_rate
+							'incoming_rate': p.get("incoming_rate")
 						}))
 			else:
 				il.append(frappe._dict({
@@ -252,7 +251,7 @@
 					'allow_zero_valuation': d.allow_zero_valuation_rate,
 					'sales_invoice_item': d.get("sales_invoice_item"),
 					'dn_detail': d.get("dn_detail"),
-					'incoming_rate': d.incoming_rate
+					'incoming_rate': d.get("incoming_rate")
 				}))
 		return il
 
@@ -312,7 +311,7 @@
 				sales_order.update_reserved_qty(so_item_rows)
 
 	def set_incoming_rate(self):
-		if self.doctype not in ("Delivery Note", "Sales Invoice"):
+		if self.doctype not in ("Delivery Note", "Sales Invoice", "Sales Order"):
 			return
 
 		items = self.get("items") + (self.get("packed_items") or [])
@@ -322,15 +321,26 @@
 				d.incoming_rate = get_incoming_rate({
 					"item_code": d.item_code,
 					"warehouse": d.warehouse,
-					"posting_date": self.posting_date,
-					"posting_time": self.posting_time,
-					"qty": -1*flt(d.qty),
-					"serial_no": d.serial_no,
+					"posting_date": self.get('posting_date') or self.get('transaction_date'),
+					"posting_time": self.get('posting_time') or nowtime(),
+					"qty": -1 * flt(d.get('stock_qty') or d.get('actual_qty')),
+					"serial_no": d.get('serial_no'),
 					"company": self.company,
 					"voucher_type": self.doctype,
 					"voucher_no": self.name,
 					"allow_zero_valuation": d.get("allow_zero_valuation")
 				}, raise_error_if_no_rate=False)
+
+				# For internal transfers use incoming rate as the valuation rate
+				if self.is_internal_transfer():
+					rate = flt(d.incoming_rate * d.conversion_factor, d.precision('rate'))
+					if d.rate != rate:
+						d.rate = rate
+						d.discount_percentage = 0
+						d.discount_amount = 0
+						frappe.msgprint(_("Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer")
+							.format(d.idx), alert=1)
+
 			elif self.get("return_against"):
 				# Get incoming rate of return entry from reference document
 				# based on original item cost as per valuation method
@@ -391,7 +401,7 @@
 				})
 				if item_row.warehouse:
 					sle.dependant_sle_voucher_detail_no = item_row.name
-			
+
 		return sle
 
 	def set_po_nos(self, for_validate=False):
@@ -446,9 +456,13 @@
 		check_list, chk_dupl_itm = [], []
 		if cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")):
 			return
+		if self.doctype == "Sales Invoice" and self.is_consolidated:
+			return
+		if self.doctype == "POS Invoice":
+			return
 
 		for d in self.get('items'):
-			if self.doctype in ["POS Invoice","Sales Invoice"]:
+			if self.doctype == "Sales Invoice":
 				stock_items = [d.item_code, d.description, d.warehouse, d.sales_order or d.delivery_note, d.batch_no or '']
 				non_stock_items = [d.item_code, d.description, d.sales_order or d.delivery_note]
 			elif self.doctype == "Delivery Note":
@@ -459,13 +473,19 @@
 				non_stock_items = [d.item_code, d.description]
 
 			if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1:
+				duplicate_items_msg = _("Item {0} entered multiple times.").format(frappe.bold(d.item_code))
+				duplicate_items_msg += "<br><br>"
+				duplicate_items_msg += _("Please enable {} in {} to allow same item in multiple rows").format(
+					frappe.bold("Allow Item to Be Added Multiple Times in a Transaction"),
+					get_link_to_form("Selling Settings", "Selling Settings")
+				)
 				if stock_items in check_list:
-					frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code))
+					frappe.throw(duplicate_items_msg)
 				else:
 					check_list.append(stock_items)
 			else:
 				if non_stock_items in chk_dupl_itm:
-					frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code))
+					frappe.throw(duplicate_items_msg)
 				else:
 					chk_dupl_itm.append(non_stock_items)
 
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 8c05134..0987d09 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -93,6 +93,12 @@
 		["Open", "eval:self.docstatus == 1 and not self.pos_closing_entry"],
 		["Closed", "eval:self.docstatus == 1 and self.pos_closing_entry"],
 		["Cancelled", "eval:self.docstatus == 2"],
+	],
+	"POS Closing Entry": [
+		["Draft", None],
+		["Submitted", "eval:self.docstatus == 1"],
+		["Queued", "eval:self.status == 'Queued'"],
+		["Cancelled", "eval:self.docstatus == 2"],
 	]
 }
 
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 4399976..cb44b73 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -6,6 +6,7 @@
 from frappe.utils import cint, flt, cstr, get_link_to_form, today, getdate
 from frappe import _
 import frappe.defaults
+from collections import defaultdict
 from erpnext.accounts.utils import get_fiscal_year, check_if_stock_and_account_balance_synced
 from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries, process_gl_map
 from erpnext.controllers.accounts_controller import AccountsController
@@ -23,6 +24,9 @@
 			self.validate_inspection()
 		self.validate_serialized_batch()
 		self.validate_customer_provided_item()
+		self.set_rate_of_stock_uom()
+		self.validate_internal_transfer()
+		self.validate_putaway_capacity()
 
 	def make_gl_entries(self, gl_entries=None, from_repost=False):
 		if self.docstatus == 2:
@@ -70,8 +74,9 @@
 
 		gl_list = []
 		warehouse_with_no_account = []
-		precision = frappe.get_precision("GL Entry", "debit_in_account_currency")
+		precision = self.get_debit_field_precision()
 		for item_row in voucher_details:
+
 			sle_list = sle_map.get(item_row.name)
 			if sle_list:
 				for sle in sle_list:
@@ -126,7 +131,13 @@
 				if frappe.db.get_value("Warehouse", wh, "company"):
 					frappe.throw(_("Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}.").format(wh, self.company))
 
-		return process_gl_map(gl_list)
+		return process_gl_map(gl_list, precision=precision)
+
+	def get_debit_field_precision(self):
+		if not frappe.flags.debit_field_precision:
+			frappe.flags.debit_field_precision = frappe.get_precision("GL Entry", "debit_in_account_currency")
+
+		return frappe.flags.debit_field_precision
 
 	def update_stock_ledger_entries(self, sle):
 		sle.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
@@ -216,7 +227,7 @@
 		""", (self.doctype, self.name), as_dict=True)
 
 		for sle in stock_ledger_entries:
-				stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
+			stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
 		return stock_ledger
 
 	def make_batches(self, warehouse_field):
@@ -239,7 +250,7 @@
 				.format(item.idx, frappe.bold(item.item_code), msg), title=_("Expense Account Missing"))
 
 		else:
-			is_expense_account = frappe.db.get_value("Account",
+			is_expense_account = frappe.get_cached_value("Account",
 				item.get("expense_account"), "report_type")=="Profit and Loss"
 			if self.doctype not in ("Purchase Receipt", "Purchase Invoice", "Stock Reconciliation", "Stock Entry") and not is_expense_account:
 				frappe.throw(_("Expense / Difference account ({0}) must be a 'Profit or Loss' account")
@@ -391,6 +402,89 @@
 			if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
 				d.allow_zero_valuation_rate = 1
 
+	def set_rate_of_stock_uom(self):
+		if self.doctype in ["Purchase Receipt", "Purchase Invoice", "Purchase Order", "Sales Invoice", "Sales Order", "Delivery Note", "Quotation"]:
+			for d in self.get("items"):
+				d.stock_uom_rate = d.rate / d.conversion_factor
+
+	def validate_internal_transfer(self):
+		if self.doctype in ('Sales Invoice', 'Delivery Note', 'Purchase Invoice', 'Purchase Receipt') \
+			and self.is_internal_transfer():
+			self.validate_in_transit_warehouses()
+			self.validate_multi_currency()
+			self.validate_packed_items()
+
+	def validate_in_transit_warehouses(self):
+		if (self.doctype == 'Sales Invoice' and self.get('update_stock')) or self.doctype == 'Delivery Note':
+			for item in self.get('items'):
+				if not item.target_warehouse:
+					frappe.throw(_("Row {0}: Target Warehouse is mandatory for internal transfers").format(item.idx))
+
+		if (self.doctype == 'Purchase Invoice' and self.get('update_stock')) or self.doctype == 'Purchase Receipt':
+			for item in self.get('items'):
+				if not item.from_warehouse:
+					frappe.throw(_("Row {0}: From Warehouse is mandatory for internal transfers").format(item.idx))
+
+	def validate_multi_currency(self):
+		if self.currency != self.company_currency:
+			frappe.throw(_("Internal transfers can only be done in company's default currency"))
+
+	def validate_packed_items(self):
+		if self.doctype in ('Sales Invoice', 'Delivery Note Item') and self.get('packed_items'):
+			frappe.throw(_("Packed Items cannot be transferred internally"))
+
+	def validate_putaway_capacity(self):
+		# if over receipt is attempted while 'apply putaway rule' is disabled
+		# and if rule was applied on the transaction, validate it.
+		from erpnext.stock.doctype.putaway_rule.putaway_rule import get_available_putaway_capacity
+		valid_doctype = self.doctype in ("Purchase Receipt", "Stock Entry", "Purchase Invoice",
+			"Stock Reconciliation")
+
+		if self.doctype == "Purchase Invoice" and self.get("update_stock") == 0:
+			valid_doctype = False
+
+		if valid_doctype:
+			rule_map = defaultdict(dict)
+			for item in self.get("items"):
+				warehouse_field = "t_warehouse" if self.doctype == "Stock Entry" else "warehouse"
+				rule = frappe.db.get_value("Putaway Rule",
+					{
+						"item_code": item.get("item_code"),
+						"warehouse": item.get(warehouse_field)
+					},
+					["name", "disable"], as_dict=True)
+				if rule:
+					if rule.get("disabled"): continue # dont validate for disabled rule
+
+					if self.doctype == "Stock Reconciliation":
+						stock_qty = flt(item.qty)
+					else:
+						stock_qty = flt(item.transfer_qty) if self.doctype == "Stock Entry" else flt(item.stock_qty)
+
+					rule_name = rule.get("name")
+					if not rule_map[rule_name]:
+						rule_map[rule_name]["warehouse"] = item.get(warehouse_field)
+						rule_map[rule_name]["item"] = item.get("item_code")
+						rule_map[rule_name]["qty_put"] = 0
+						rule_map[rule_name]["capacity"] = get_available_putaway_capacity(rule_name)
+					rule_map[rule_name]["qty_put"] += flt(stock_qty)
+
+			for rule, values in rule_map.items():
+				if flt(values["qty_put"]) > flt(values["capacity"]):
+					message = self.prepare_over_receipt_message(rule, values)
+					frappe.throw(msg=message, title=_("Over Receipt"))
+
+	def prepare_over_receipt_message(self, rule, values):
+		message = _("{0} qty of Item {1} is being received into Warehouse {2} with capacity {3}.") \
+			.format(
+				frappe.bold(values["qty_put"]), frappe.bold(values["item"]),
+				frappe.bold(values["warehouse"]), frappe.bold(values["capacity"])
+			)
+		message += "<br><br>"
+		rule_link = frappe.utils.get_link_to_form("Putaway Rule", rule)
+		message += _(" Please adjust the qty or edit {0} to proceed.").format(rule_link)
+		return message
+
 	def repost_future_sle_and_gle(self):
 		args = frappe._dict({
 			"posting_date": self.posting_date,
@@ -399,7 +493,6 @@
 			"voucher_no": self.name,
 			"company": self.company
 		})
-
 		if check_if_future_sle_exists(args):
 			create_repost_item_valuation_entry(args)
 		elif not is_reposting_pending():
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 8dd2e5b..6c7eb92 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -10,6 +10,7 @@
 	validate_taxes_and_charges, validate_inclusive_tax
 from erpnext.stock.get_item_details import _get_item_tax_template
 from erpnext.accounts.doctype.pricing_rule.utils import get_applied_pricing_rules
+from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate
 
 class calculate_taxes_and_totals(object):
 	def __init__(self, doc):
@@ -106,7 +107,7 @@
 					elif item.discount_amount and item.pricing_rules:
 						item.rate =  item.price_list_rate - item.discount_amount
 
-				if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item']:
+				if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item', 'POS Invoice Item']:
 					item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
 					if flt(item.rate_with_margin) > 0:
 						item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
@@ -615,7 +616,6 @@
 				self.doc.precision("base_write_off_amount"))
 
 	def calculate_margin(self, item):
-
 		rate_with_margin = 0.0
 		base_rate_with_margin = 0.0
 		if item.price_list_rate:
@@ -624,8 +624,8 @@
 				for d in get_applied_pricing_rules(item.pricing_rules):
 					pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
 
-					if (pricing_rule.margin_type in ['Amount', 'Percentage'] and pricing_rule.currency == self.doc.currency)\
-							or (pricing_rule.margin_type == 'Percentage'):
+					if pricing_rule.margin_rate_or_amount and ((pricing_rule.currency == self.doc.currency and
+						pricing_rule.margin_type in ['Amount', 'Percentage']) or pricing_rule.margin_type == 'Percentage'):
 						item.margin_type = pricing_rule.margin_type
 						item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
 						has_margin = True
@@ -758,3 +758,35 @@
 	for taxes in itemised_tax.values():
 		for tax_account in taxes:
 			taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
+
+class init_landed_taxes_and_totals(object):
+	def __init__(self, doc):
+		self.doc = doc
+		self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
+		self.set_account_currency()
+		self.set_exchange_rate()
+		self.set_amounts_in_company_currency()
+
+	def set_account_currency(self):
+		company_currency = erpnext.get_company_currency(self.doc.company)
+		for d in self.doc.get(self.tax_field):
+			if not d.account_currency:
+				account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency')
+				d.account_currency = account_currency or company_currency
+
+	def set_exchange_rate(self):
+		company_currency = erpnext.get_company_currency(self.doc.company)
+		for d in self.doc.get(self.tax_field):
+			if d.account_currency == company_currency:
+				d.exchange_rate = 1
+			elif not d.exchange_rate or d.exchange_rate == 1 or self.doc.posting_date:
+				d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
+					account_currency=d.account_currency, company=self.doc.company)
+
+			if not d.exchange_rate:
+				frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
+
+	def set_amounts_in_company_currency(self):
+		for d in self.doc.get(self.tax_field):
+			d.amount = flt(d.amount, d.precision("amount"))
+			d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))
\ No newline at end of file
diff --git a/erpnext/controllers/tests/test_item_variant.py b/erpnext/controllers/tests/test_item_variant.py
index c257215..813f0a0 100644
--- a/erpnext/controllers/tests/test_item_variant.py
+++ b/erpnext/controllers/tests/test_item_variant.py
@@ -6,6 +6,7 @@
 
 from erpnext.stock.doctype.item.test_item import set_item_variant_settings
 from erpnext.controllers.item_variant import copy_attributes_to_variant, make_variant_item_code
+from erpnext.stock.doctype.quality_inspection.test_quality_inspection import create_quality_inspection_parameter
 
 from six import string_types
 
@@ -56,6 +57,8 @@
 
 	qc = frappe.new_doc("Quality Inspection Template")
 	qc.quality_inspection_template_name = qc_template
+
+	create_quality_inspection_parameter("Moisture")
 	qc.append('item_quality_inspection_parameter', {
 		"specification": "Moisture",
 		"value": "&lt; 5%",
diff --git a/erpnext/crm/doctype/appointment/appointment.py b/erpnext/crm/doctype/appointment/appointment.py
index 63efeb3..2009ebf 100644
--- a/erpnext/crm/doctype/appointment/appointment.py
+++ b/erpnext/crm/doctype/appointment/appointment.py
@@ -126,7 +126,7 @@
 			add_assignemnt({
 				'doctype': self.doctype,
 				'name': self.name,
-				'assign_to': existing_assignee
+				'assign_to': [existing_assignee]
 			})
 			return
 		if self._assign:
@@ -139,7 +139,7 @@
 				add_assignemnt({
 					'doctype': self.doctype,
 					'name': self.name,
-					'assign_to': agent
+					'assign_to': [agent]
 				})
 			break
 
diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py
index 1439adb..938cbfd 100644
--- a/erpnext/crm/doctype/lead/lead.py
+++ b/erpnext/crm/doctype/lead/lead.py
@@ -176,7 +176,7 @@
 				"phone": self.mobile_no
 			})
 
-		contact.insert()
+		contact.insert(ignore_permissions=True)
 
 		return contact
 
diff --git a/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json b/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json
index 9f996d9..0ee9317 100644
--- a/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json
+++ b/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json
@@ -8,12 +8,12 @@
  "is_mandatory": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2020-05-14 17:38:27.496696",
+ "modified": "2021-01-21 15:28:52.483839",
  "modified_by": "Administrator",
  "name": "Create Opportunity",
  "owner": "Administrator",
  "reference_document": "Opportunity",
- "show_full_form": 0,
+ "show_full_form": 1,
  "title": "Create Opportunity",
  "validate_action": 1
 }
\ No newline at end of file
diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.js b/erpnext/education/doctype/fee_schedule/fee_schedule.js
index 75dd446..65b5fa6 100644
--- a/erpnext/education/doctype/fee_schedule/fee_schedule.js
+++ b/erpnext/education/doctype/fee_schedule/fee_schedule.js
@@ -1,6 +1,7 @@
 // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 
+frappe.provide("erpnext.accounts.dimensions");
 frappe.ui.form.on('Fee Schedule', {
 	setup: function(frm) {
 		frm.add_fetch('fee_structure', 'receivable_account', 'receivable_account');
@@ -8,6 +9,10 @@
 		frm.add_fetch('fee_structure', 'cost_center', 'cost_center');
 	},
 
+	company: function(frm) {
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
+	},
+
 	onload: function(frm) {
 		frm.set_query('receivable_account', function(doc) {
 			return {
@@ -50,6 +55,8 @@
 				}
 			}
 		});
+
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
 	},
 
 	refresh: function(frm) {
diff --git a/erpnext/education/doctype/fee_structure/fee_structure.js b/erpnext/education/doctype/fee_structure/fee_structure.js
index b331c6d..310c410 100644
--- a/erpnext/education/doctype/fee_structure/fee_structure.js
+++ b/erpnext/education/doctype/fee_structure/fee_structure.js
@@ -1,6 +1,8 @@
 // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 
+frappe.provide("erpnext.accounts.dimensions");
+
 frappe.ui.form.on('Fee Structure', {
 	setup: function(frm) {
 		frm.add_fetch('company', 'default_receivable_account', 'receivable_account');
@@ -8,6 +10,10 @@
 		frm.add_fetch('company', 'cost_center', 'cost_center');
 	},
 
+	company: function(frm) {
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
+	},
+
 	onload: function(frm) {
 		frm.set_query('academic_term', function() {
 			return {
@@ -35,6 +41,8 @@
 				}
 			};
 		});
+
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
 	},
 
 	refresh: function(frm) {
diff --git a/erpnext/education/doctype/fees/fees.js b/erpnext/education/doctype/fees/fees.js
index aaf42b4..ac66acd 100644
--- a/erpnext/education/doctype/fees/fees.js
+++ b/erpnext/education/doctype/fees/fees.js
@@ -1,6 +1,7 @@
 // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 
+frappe.provide("erpnext.accounts.dimensions");
 
 frappe.ui.form.on("Fees", {
 	setup: function(frm) {
@@ -9,15 +10,19 @@
 		frm.add_fetch("fee_structure", "cost_center", "cost_center");
 	},
 
-	onload: function(frm){
-		frm.set_query("academic_term",function(){
+	company: function(frm) {
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
+	},
+
+	onload: function(frm) {
+		frm.set_query("academic_term", function() {
 			return{
-				"filters":{
+				"filters": {
 					"academic_year": (frm.doc.academic_year)
 				}
 			};
 		});
-		frm.set_query("fee_structure",function(){
+		frm.set_query("fee_structure", function() {
 			return{
 				"filters":{
 					"academic_year": (frm.doc.academic_year)
@@ -45,6 +50,8 @@
 		if (!frm.doc.posting_date) {
 			frm.doc.posting_date = frappe.datetime.get_today();
 		}
+
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
 	},
 
 	refresh: function(frm) {
diff --git a/erpnext/education/doctype/program_enrollment/program_enrollment.py b/erpnext/education/doctype/program_enrollment/program_enrollment.py
index 6fbcd8a..886a7d8 100644
--- a/erpnext/education/doctype/program_enrollment/program_enrollment.py
+++ b/erpnext/education/doctype/program_enrollment/program_enrollment.py
@@ -124,21 +124,24 @@
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_program_courses(doctype, txt, searchfield, start, page_len, filters):
-	if filters.get('program'):
-		return frappe.db.sql("""select course, course_name from `tabProgram Course`
-			where  parent = %(program)s and course like %(txt)s {match_cond}
-			order by
-				if(locate(%(_txt)s, course), locate(%(_txt)s, course), 99999),
-				idx desc,
-				`tabProgram Course`.course asc
-			limit {start}, {page_len}""".format(
-				match_cond=get_match_cond(doctype),
-				start=start,
-				page_len=page_len), {
-					"txt": "%{0}%".format(txt),
-					"_txt": txt.replace('%', ''),
-					"program": filters['program']
-				})
+	if not filters.get('program'):
+		frappe.msgprint(_("Please select a Program first."))
+		return []
+
+	return frappe.db.sql("""select course, course_name from `tabProgram Course`
+		where  parent = %(program)s and course like %(txt)s {match_cond}
+		order by
+			if(locate(%(_txt)s, course), locate(%(_txt)s, course), 99999),
+			idx desc,
+			`tabProgram Course`.course asc
+		limit {start}, {page_len}""".format(
+			match_cond=get_match_cond(doctype),
+			start=start,
+			page_len=page_len), {
+				"txt": "%{0}%".format(txt),
+				"_txt": txt.replace('%', ''),
+				"program": filters['program']
+			})
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
index d33b0a7..554c6b0 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
@@ -5,7 +5,7 @@
 
 class MpesaConnector():
 	def __init__(self, env="sandbox", app_key=None, app_secret=None, sandbox_url="https://sandbox.safaricom.co.ke",
-		live_url="https://safaricom.co.ke"):
+		live_url="https://api.safaricom.co.ke"):
 		"""Setup configuration for Mpesa connector and generate new access token."""
 		self.env = env
 		self.app_key = app_key
@@ -102,14 +102,14 @@
 			"BusinessShortCode": business_shortcode,
 			"Password": encoded.decode("utf-8"),
 			"Timestamp": time,
-			"TransactionType": "CustomerPayBillOnline",
 			"Amount": amount,
 			"PartyA": int(phone_number),
-			"PartyB": business_shortcode,
+			"PartyB": reference_code,
 			"PhoneNumber": int(phone_number),
 			"CallBackURL": callback_url,
 			"AccountReference": reference_code,
-			"TransactionDesc": description
+			"TransactionDesc": description,
+			"TransactionType": "CustomerPayBillOnline" if self.env == "sandbox" else "CustomerBuyGoodsOnline"
 		}
 		headers = {'Authorization': 'Bearer {0}'.format(self.authentication_token), 'Content-Type': "application/json"}
 
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json
index fc7b310..407f826 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json
@@ -11,8 +11,10 @@
   "consumer_secret",
   "initiator_name",
   "till_number",
+  "transaction_limit",
   "sandbox",
   "column_break_4",
+  "business_shortcode",
   "online_passkey",
   "security_credential",
   "get_account_balance",
@@ -84,10 +86,24 @@
    "fieldname": "get_account_balance",
    "fieldtype": "Button",
    "label": "Get Account Balance"
+  },
+  {
+   "depends_on": "eval:(doc.sandbox==0)",
+   "fieldname": "business_shortcode",
+   "fieldtype": "Data",
+   "label": "Business Shortcode",
+   "mandatory_depends_on": "eval:(doc.sandbox==0)"
+  },
+  {
+   "default": "150000",
+   "fieldname": "transaction_limit",
+   "fieldtype": "Float",
+   "label": "Transaction Limit",
+   "non_negative": 1
   }
  ],
  "links": [],
- "modified": "2020-09-25 20:21:38.215494",
+ "modified": "2021-01-29 12:02:16.106942",
  "modified_by": "Administrator",
  "module": "ERPNext Integrations",
  "name": "Mpesa Settings",
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
index 1cad84d..b571802 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
@@ -33,13 +33,34 @@
 		create_mode_of_payment('Mpesa-' + self.payment_gateway_name, payment_type="Phone")
 
 	def request_for_payment(self, **kwargs):
-		if frappe.flags.in_test:
-			from erpnext.erpnext_integrations.doctype.mpesa_settings.test_mpesa_settings import get_payment_request_response_payload
-			response = frappe._dict(get_payment_request_response_payload())
-		else:
-			response = frappe._dict(generate_stk_push(**kwargs))
+		args = frappe._dict(kwargs)
+		request_amounts = self.split_request_amount_according_to_transaction_limit(args)
 
-		self.handle_api_response("CheckoutRequestID", kwargs, response)
+		for i, amount in enumerate(request_amounts):
+			args.request_amount = amount
+			if frappe.flags.in_test:
+				from erpnext.erpnext_integrations.doctype.mpesa_settings.test_mpesa_settings import get_payment_request_response_payload
+				response = frappe._dict(get_payment_request_response_payload(amount))
+			else:
+				response = frappe._dict(generate_stk_push(**args))
+
+			self.handle_api_response("CheckoutRequestID", args, response)
+
+	def split_request_amount_according_to_transaction_limit(self, args):
+		request_amount = args.request_amount
+		if request_amount > self.transaction_limit:
+			# make multiple requests
+			request_amounts = []
+			requests_to_be_made = frappe.utils.ceil(request_amount / self.transaction_limit) # 480/150 = ceil(3.2) = 4
+			for i in range(requests_to_be_made):
+				amount = self.transaction_limit
+				if i == requests_to_be_made - 1:
+					amount = request_amount - (self.transaction_limit * i) # for 4th request, 480 - (150 * 3) = 30
+				request_amounts.append(amount)
+		else:
+			request_amounts = [request_amount]
+		
+		return request_amounts
 
 	def get_account_balance_info(self):
 		payload = dict(
@@ -67,7 +88,8 @@
 			req_name = getattr(response, global_id)
 			error = None
 
-		create_request_log(request_dict, "Host", "Mpesa", req_name, error)
+		if not frappe.db.exists('Integration Request', req_name):
+			create_request_log(request_dict, "Host", "Mpesa", req_name, error)
 
 		if error:
 			frappe.throw(_(getattr(response, "errorMessage")), title=_("Transaction Error"))
@@ -80,6 +102,8 @@
 
 		mpesa_settings = frappe.get_doc("Mpesa Settings", args.payment_gateway[6:])
 		env = "production" if not mpesa_settings.sandbox else "sandbox"
+		# for sandbox, business shortcode is same as till number
+		business_shortcode = mpesa_settings.business_shortcode if env == "production" else mpesa_settings.till_number
 
 		connector = MpesaConnector(env=env,
 			app_key=mpesa_settings.consumer_key,
@@ -87,10 +111,12 @@
 
 		mobile_number = sanitize_mobile_number(args.sender)
 
-		response = connector.stk_push(business_shortcode=mpesa_settings.till_number,
-			passcode=mpesa_settings.get_password("online_passkey"), amount=args.grand_total,
+		response = connector.stk_push(
+			business_shortcode=business_shortcode, amount=args.request_amount,
+			passcode=mpesa_settings.get_password("online_passkey"),
 			callback_url=callback_url, reference_code=mpesa_settings.till_number,
-			phone_number=mobile_number, description="POS Payment")
+			phone_number=mobile_number, description="POS Payment"
+		)
 
 		return response
 
@@ -108,29 +134,72 @@
 	transaction_response = frappe._dict(kwargs["Body"]["stkCallback"])
 
 	checkout_id = getattr(transaction_response, "CheckoutRequestID", "")
-	request = frappe.get_doc("Integration Request", checkout_id)
-	transaction_data = frappe._dict(loads(request.data))
+	integration_request = frappe.get_doc("Integration Request", checkout_id)
+	transaction_data = frappe._dict(loads(integration_request.data))
+	total_paid = 0 # for multiple integration request made against a pos invoice
+	success = False # for reporting successfull callback to point of sale ui
 
 	if transaction_response['ResultCode'] == 0:
-		if request.reference_doctype and request.reference_docname:
+		if integration_request.reference_doctype and integration_request.reference_docname:
 			try:
-				doc = frappe.get_doc(request.reference_doctype,
-					request.reference_docname)
-				doc.run_method("on_payment_authorized", 'Completed')
-
 				item_response = transaction_response["CallbackMetadata"]["Item"]
+				amount = fetch_param_value(item_response, "Amount", "Name")
 				mpesa_receipt = fetch_param_value(item_response, "MpesaReceiptNumber", "Name")
-				frappe.db.set_value("POS Invoice", doc.reference_name, "mpesa_receipt_number", mpesa_receipt)
-				request.handle_success(transaction_response)
+				pr = frappe.get_doc(integration_request.reference_doctype, integration_request.reference_docname)
+
+				mpesa_receipts, completed_payments = get_completed_integration_requests_info(
+					integration_request.reference_doctype,
+					integration_request.reference_docname,
+					checkout_id
+				)
+
+				total_paid = amount + sum(completed_payments)
+				mpesa_receipts = ', '.join(mpesa_receipts + [mpesa_receipt])
+
+				if total_paid >= pr.grand_total:
+					pr.run_method("on_payment_authorized", 'Completed')
+					success = True
+
+				frappe.db.set_value("POS Invoice", pr.reference_name, "mpesa_receipt_number", mpesa_receipts)
+				integration_request.handle_success(transaction_response)
 			except Exception:
-				request.handle_failure(transaction_response)
+				integration_request.handle_failure(transaction_response)
 				frappe.log_error(frappe.get_traceback())
 
 	else:
-		request.handle_failure(transaction_response)
+		integration_request.handle_failure(transaction_response)
 
-	frappe.publish_realtime('process_phone_payment', doctype="POS Invoice",
-		docname=transaction_data.payment_reference, user=request.owner, message=transaction_response)
+	frappe.publish_realtime(
+		event='process_phone_payment',
+		doctype="POS Invoice",
+		docname=transaction_data.payment_reference,
+		user=integration_request.owner,
+		message={
+			'amount': total_paid,
+			'success': success,
+			'failure_message': transaction_response["ResultDesc"] if transaction_response['ResultCode'] != 0 else ''
+		},
+	)
+
+def get_completed_integration_requests_info(reference_doctype, reference_docname, checkout_id):
+	output_of_other_completed_requests = frappe.get_all("Integration Request", filters={
+		'name': ['!=', checkout_id],
+		'reference_doctype': reference_doctype,
+		'reference_docname': reference_docname,
+		'status': 'Completed'
+	}, pluck="output")
+
+	mpesa_receipts, completed_payments = [], []
+
+	for out in output_of_other_completed_requests:
+		out = frappe._dict(loads(out))
+		item_response = out["CallbackMetadata"]["Item"]
+		completed_amount = fetch_param_value(item_response, "Amount", "Name")
+		completed_mpesa_receipt = fetch_param_value(item_response, "MpesaReceiptNumber", "Name")
+		completed_payments.append(completed_amount)
+		mpesa_receipts.append(completed_mpesa_receipt)
+	
+	return mpesa_receipts, completed_payments
 
 def get_account_balance(request_payload):
 	"""Call account balance API to send the request to the Mpesa Servers."""
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
index 4e86d36..08b2bc2 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
@@ -9,6 +9,10 @@
 from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
 
 class TestMpesaSettings(unittest.TestCase):
+	def tearDown(self):
+		frappe.db.sql('delete from `tabMpesa Settings`')
+		frappe.db.sql('delete from `tabIntegration Request` where integration_request_service = "Mpesa"')
+
 	def test_creation_of_payment_gateway(self):
 		create_mpesa_settings(payment_gateway_name="_Test")
 
@@ -40,10 +44,13 @@
 			}
 		}))
 
+		integration_request.delete()
+
 	def test_processing_of_callback_payload(self):
 		create_mpesa_settings(payment_gateway_name="Payment")
 		mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
 		frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
+		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES")
 
 		pos_invoice = create_pos_invoice(do_not_submit=1)
 		pos_invoice.append("payments", {'mode_of_payment': 'Mpesa-Payment', 'account': mpesa_account, 'amount': 500})
@@ -55,10 +62,16 @@
 		# test payment request creation
 		self.assertEquals(pr.payment_gateway, "Mpesa-Payment")
 
-		callback_response = get_payment_callback_payload()
+		# submitting payment request creates integration requests with random id
+		integration_req_ids = frappe.get_all("Integration Request", filters={
+			'reference_doctype': pr.doctype,
+			'reference_docname': pr.name,
+		}, pluck="name")
+
+		callback_response = get_payment_callback_payload(Amount=500, CheckoutRequestID=integration_req_ids[0])
 		verify_transaction(**callback_response)
 		# test creation of integration request
-		integration_request = frappe.get_doc("Integration Request", "ws_CO_061020201133231972")
+		integration_request = frappe.get_doc("Integration Request", integration_req_ids[0])
 
 		# test integration request creation and successful update of the status on receiving callback response
 		self.assertTrue(integration_request)
@@ -68,6 +81,122 @@
 		integration_request.reload()
 		self.assertEquals(pos_invoice.mpesa_receipt_number, "LGR7OWQX0R")
 		self.assertEquals(integration_request.status, "Completed")
+		
+		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "")
+		integration_request.delete()
+		pr.reload()
+		pr.cancel()
+		pr.delete()
+		pos_invoice.delete()
+
+	def test_processing_of_multiple_callback_payload(self):
+		create_mpesa_settings(payment_gateway_name="Payment")
+		mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
+		frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
+		frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500")
+		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES")
+
+		pos_invoice = create_pos_invoice(do_not_submit=1)
+		pos_invoice.append("payments", {'mode_of_payment': 'Mpesa-Payment', 'account': mpesa_account, 'amount': 1000})
+		pos_invoice.contact_mobile = "093456543894"
+		pos_invoice.currency = "KES"
+		pos_invoice.save()
+
+		pr = pos_invoice.create_payment_request()
+		# test payment request creation
+		self.assertEquals(pr.payment_gateway, "Mpesa-Payment")
+
+		# submitting payment request creates integration requests with random id
+		integration_req_ids = frappe.get_all("Integration Request", filters={
+			'reference_doctype': pr.doctype,
+			'reference_docname': pr.name,
+		}, pluck="name")
+
+		# create random receipt nos and send it as response to callback handler
+		mpesa_receipt_numbers = [frappe.utils.random_string(5) for d in integration_req_ids]
+
+		integration_requests = []
+		for i in range(len(integration_req_ids)):
+			callback_response = get_payment_callback_payload(
+				Amount=500,
+				CheckoutRequestID=integration_req_ids[i],
+				MpesaReceiptNumber=mpesa_receipt_numbers[i]
+			)
+			# handle response manually
+			verify_transaction(**callback_response)
+			# test completion of integration request
+			integration_request = frappe.get_doc("Integration Request", integration_req_ids[i])
+			self.assertEquals(integration_request.status, "Completed")
+			integration_requests.append(integration_request)
+
+		# check receipt number once all the integration requests are completed
+		pos_invoice.reload()
+		self.assertEquals(pos_invoice.mpesa_receipt_number, ', '.join(mpesa_receipt_numbers))
+
+		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "")
+		[d.delete() for d in integration_requests]
+		pr.reload()
+		pr.cancel()
+		pr.delete()
+		pos_invoice.delete()
+	
+	def test_processing_of_only_one_succes_callback_payload(self):
+		create_mpesa_settings(payment_gateway_name="Payment")
+		mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
+		frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
+		frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500")
+		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES")
+
+		pos_invoice = create_pos_invoice(do_not_submit=1)
+		pos_invoice.append("payments", {'mode_of_payment': 'Mpesa-Payment', 'account': mpesa_account, 'amount': 1000})
+		pos_invoice.contact_mobile = "093456543894"
+		pos_invoice.currency = "KES"
+		pos_invoice.save()
+
+		pr = pos_invoice.create_payment_request()
+		# test payment request creation
+		self.assertEquals(pr.payment_gateway, "Mpesa-Payment")
+
+		# submitting payment request creates integration requests with random id
+		integration_req_ids = frappe.get_all("Integration Request", filters={
+			'reference_doctype': pr.doctype,
+			'reference_docname': pr.name,
+		}, pluck="name")
+
+		# create random receipt nos and send it as response to callback handler
+		mpesa_receipt_numbers = [frappe.utils.random_string(5) for d in integration_req_ids]
+
+		callback_response = get_payment_callback_payload(
+			Amount=500,
+			CheckoutRequestID=integration_req_ids[0],
+			MpesaReceiptNumber=mpesa_receipt_numbers[0]
+		)
+		# handle response manually
+		verify_transaction(**callback_response)
+		# test completion of integration request
+		integration_request = frappe.get_doc("Integration Request", integration_req_ids[0])
+		self.assertEquals(integration_request.status, "Completed")
+
+		# now one request is completed
+		# second integration request fails
+		# now retrying payment request should make only one integration request again
+		pr = pos_invoice.create_payment_request()
+		new_integration_req_ids = frappe.get_all("Integration Request", filters={
+			'reference_doctype': pr.doctype,
+			'reference_docname': pr.name,
+			'name': ['not in', integration_req_ids]
+		}, pluck="name")
+
+		self.assertEquals(len(new_integration_req_ids), 1)
+
+		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "")
+		frappe.db.sql("delete from `tabIntegration Request` where integration_request_service = 'Mpesa'")
+		pr.reload()
+		pr.cancel()
+		pr.delete()
+		pos_invoice.delete()
+
+		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "")
 
 def create_mpesa_settings(payment_gateway_name="Express"):
 	if frappe.db.exists("Mpesa Settings", payment_gateway_name):
@@ -157,16 +286,19 @@
 	}
 		}
 
-def get_payment_request_response_payload():
+def get_payment_request_response_payload(Amount=500):
 	"""Response received after successfully calling the stk push process request API."""
+
+	CheckoutRequestID = frappe.utils.random_string(10)
+
 	return {
 		"MerchantRequestID": "8071-27184008-1",
-		"CheckoutRequestID": "ws_CO_061020201133231972",
+		"CheckoutRequestID": CheckoutRequestID,
 		"ResultCode": 0,
 		"ResultDesc": "The service request is processed successfully.",
 		"CallbackMetadata": {
 			"Item": [
-				{ "Name": "Amount", "Value": 500.0 },
+				{ "Name": "Amount", "Value": Amount },
 				{ "Name": "MpesaReceiptNumber", "Value": "LGR7OWQX0R" },
 				{ "Name": "TransactionDate", "Value": 20201006113336 },
 				{ "Name": "PhoneNumber", "Value": 254723575670 }
@@ -174,41 +306,26 @@
 		}
 	}
 
-
-def get_payment_callback_payload():
+def get_payment_callback_payload(Amount=500, CheckoutRequestID="ws_CO_061020201133231972", MpesaReceiptNumber="LGR7OWQX0R"):
 	"""Response received from the server as callback after calling the stkpush process request API."""
 	return {
 		"Body":{
-		"stkCallback":{
-			"MerchantRequestID":"19465-780693-1",
-			"CheckoutRequestID":"ws_CO_061020201133231972",
-			"ResultCode":0,
-			"ResultDesc":"The service request is processed successfully.",
-			"CallbackMetadata":{
-			"Item":[
-				{
-				"Name":"Amount",
-				"Value":500
-				},
-				{
-				"Name":"MpesaReceiptNumber",
-				"Value":"LGR7OWQX0R"
-				},
-				{
-				"Name":"Balance"
-				},
-				{
-				"Name":"TransactionDate",
-				"Value":20170727154800
-				},
-				{
-				"Name":"PhoneNumber",
-				"Value":254721566839
+			"stkCallback":{
+				"MerchantRequestID":"19465-780693-1",
+				"CheckoutRequestID":CheckoutRequestID,
+				"ResultCode":0,
+				"ResultDesc":"The service request is processed successfully.",
+				"CallbackMetadata":{
+					"Item":[
+						{ "Name":"Amount", "Value":Amount },
+						{ "Name":"MpesaReceiptNumber", "Value":MpesaReceiptNumber },
+						{ "Name":"Balance" },
+						{ "Name":"TransactionDate", "Value":20170727154800 },
+						{ "Name":"PhoneNumber", "Value":254721566839 }
+					]
 				}
-			]
 			}
 		}
-		}
 	}
 
 def get_account_balance_callback_payload():
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
index 8d4b510..66d0e5f 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
@@ -29,14 +29,11 @@
 		response = self.client.Item.public_token.exchange(public_token)
 		access_token = response["access_token"]
 		return access_token
-
-	def get_link_token(self):
+	
+	def get_token_request(self, update_mode=False):
 		country_codes = ["US", "CA", "FR", "IE", "NL", "ES", "GB"] if self.settings.enable_european_access else ["US", "CA"]
-		token_request = {
+		args = {
 			"client_name": self.client_name,
-			"client_id": self.settings.plaid_client_id,
-			"secret": self.settings.plaid_secret,
-			"products": self.products,
 			# only allow Plaid-supported languages and countries (LAST: Sep-19-2020)
 			"language": frappe.local.lang if frappe.local.lang in ["en", "fr", "es", "nl"] else "en",
 			"country_codes": country_codes,
@@ -45,6 +42,20 @@
 			}
 		}
 
+		if update_mode:
+			args["access_token"] = self.access_token
+		else:
+			args.update({
+				"client_id": self.settings.plaid_client_id,
+				"secret": self.settings.plaid_secret,
+				"products": self.products,
+			})
+		
+		return args
+
+	def get_link_token(self, update_mode=False):
+		token_request = self.get_token_request(update_mode)
+
 		try:
 			response = self.client.LinkToken.create(token_request)
 		except InvalidRequestError:
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js
index 22a4004..bbc2ca8 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js
@@ -12,9 +12,25 @@
 
 	refresh: function (frm) {
 		if (frm.doc.enabled) {
-			frm.add_custom_button('Link a new bank account', () => {
+			frm.add_custom_button(__('Link a new bank account'), () => {
 				new erpnext.integrations.plaidLink(frm);
 			});
+
+			frm.add_custom_button(__("Sync Now"), () => {
+				frappe.call({
+					method: "erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.enqueue_synchronization",
+					freeze: true,
+					callback: () => {
+						let bank_transaction_link = '<a href="#List/Bank Transaction">Bank Transaction</a>';
+
+						frappe.msgprint({
+							title: __("Sync Started"),
+							message: __("The sync has started in the background, please check the {0} list for new records.", [bank_transaction_link]),
+							alert: 1
+						});
+					}
+				});
+			}).addClass("btn-primary");
 		}
 	}
 });
@@ -30,10 +46,18 @@
 		this.product = ["auth", "transactions"];
 		this.plaid_env = this.frm.doc.plaid_env;
 		this.client_name = frappe.boot.sitename;
-		this.token = await this.frm.call("get_link_token").then(resp => resp.message);
+		this.token = await this.get_link_token();
 		this.init_plaid();
 	}
 
+	async get_link_token() {
+		const token = await this.frm.call("get_link_token").then(resp => resp.message);
+		if (!token) {
+			frappe.throw(__('Cannot retrieve link token. Check Error Log for more information'));
+		}
+		return token;
+	}
+
 	init_plaid() {
 		const me = this;
 		me.loadScript(me.plaidUrl)
@@ -78,8 +102,8 @@
 	}
 
 	onScriptError(error) {
-		frappe.msgprint("There was an issue connecting to Plaid's authentication server");
-		frappe.msgprint(error);
+		frappe.msgprint(__("There was an issue connecting to Plaid's authentication server. Check browser console for more information"));
+		console.log(error);
 	}
 
 	plaid_success(token, response) {
@@ -107,4 +131,4 @@
 			});
 		}, __("Select a company"), __("Continue"));
 	}
-};
+};
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
index e535e81..70c7f3f 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
@@ -166,7 +166,6 @@
 		related_bank = frappe.db.get_values("Bank Account", bank_account, ["bank", "integration_id"], as_dict=True)
 		access_token = frappe.db.get_value("Bank", related_bank[0].bank, "plaid_access_token")
 		account_id = related_bank[0].integration_id
-
 	else:
 		access_token = frappe.db.get_value("Bank", bank, "plaid_access_token")
 		account_id = None
@@ -228,13 +227,23 @@
 
 def automatic_synchronization():
 	settings = frappe.get_doc("Plaid Settings", "Plaid Settings")
-
 	if settings.enabled == 1 and settings.automatic_sync == 1:
-		plaid_accounts = frappe.get_all("Bank Account", filters={"integration_id": ["!=", ""]}, fields=["name", "bank"])
+		enqueue_synchronization()
 
-		for plaid_account in plaid_accounts:
-			frappe.enqueue(
-				"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions",
-				bank=plaid_account.bank,
-				bank_account=plaid_account.name
-			)
+@frappe.whitelist()
+def enqueue_synchronization():
+	plaid_accounts = frappe.get_all("Bank Account",
+		filters={"integration_id": ["!=", ""]},
+		fields=["name", "bank"])
+
+	for plaid_account in plaid_accounts:
+		frappe.enqueue(
+			"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions",
+			bank=plaid_account.bank,
+			bank_account=plaid_account.name
+		)
+
+@frappe.whitelist()
+def get_link_token_for_update(access_token):
+	plaid = PlaidConnector(access_token)
+	return plaid.get_link_token(update_mode=True)
diff --git a/erpnext/exceptions.py b/erpnext/exceptions.py
index d92af5d..04291cd 100644
--- a/erpnext/exceptions.py
+++ b/erpnext/exceptions.py
@@ -6,3 +6,5 @@
 class InvalidAccountCurrency(frappe.ValidationError): pass
 class InvalidCurrency(frappe.ValidationError): pass
 class PartyDisabled(frappe.ValidationError):pass
+class InvalidAccountDimensionError(frappe.ValidationError): pass
+class MandatoryAccountDimensionError(frappe.ValidationError): pass
diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.js b/erpnext/healthcare/doctype/appointment_type/appointment_type.js
index 15916a5..861675a 100644
--- a/erpnext/healthcare/doctype/appointment_type/appointment_type.js
+++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.js
@@ -2,4 +2,82 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on('Appointment Type', {
+	refresh: function(frm) {
+		frm.set_query('price_list', function() {
+			return {
+				filters: {'selling': 1}
+			};
+		});
+
+		frm.set_query('medical_department', 'items', function(doc) {
+			let item_list = doc.items.map(({medical_department}) => medical_department);
+			return {
+				filters: [
+					['Medical Department', 'name', 'not in', item_list]
+				]
+			};
+		});
+
+		frm.set_query('op_consulting_charge_item', 'items', function() {
+			return {
+				filters: {
+					is_stock_item: 0
+				}
+			};
+		});
+
+		frm.set_query('inpatient_visit_charge_item', 'items', function() {
+			return {
+				filters: {
+					is_stock_item: 0
+				}
+			};
+		});
+	}
 });
+
+frappe.ui.form.on('Appointment Type Service Item', {
+	op_consulting_charge_item: function(frm, cdt, cdn) {
+		let d = locals[cdt][cdn];
+		if (frm.doc.price_list && d.op_consulting_charge_item) {
+			frappe.call({
+				'method': 'frappe.client.get_value',
+				args: {
+					'doctype': 'Item Price',
+					'filters': {
+						'item_code': d.op_consulting_charge_item,
+						'price_list': frm.doc.price_list
+					},
+					'fieldname': ['price_list_rate']
+				},
+				callback: function(data) {
+					if (data.message.price_list_rate) {
+						frappe.model.set_value(cdt, cdn, 'op_consulting_charge', data.message.price_list_rate);
+					}
+				}
+			});
+		}
+	},
+
+	inpatient_visit_charge_item: function(frm, cdt, cdn) {
+		let d = locals[cdt][cdn];
+		if (frm.doc.price_list && d.inpatient_visit_charge_item) {
+			frappe.call({
+				'method': 'frappe.client.get_value',
+				args: {
+					'doctype': 'Item Price',
+					'filters': {
+						'item_code': d.inpatient_visit_charge_item,
+						'price_list': frm.doc.price_list
+					},
+					'fieldname': ['price_list_rate']
+				},
+				callback: function (data) {
+					if (data.message.price_list_rate) {
+						frappe.model.set_value(cdt, cdn, 'inpatient_visit_charge', data.message.price_list_rate);
+					}
+				}
+			});
+		}
+	}
+});
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.json b/erpnext/healthcare/doctype/appointment_type/appointment_type.json
index 58753bb..3872318 100644
--- a/erpnext/healthcare/doctype/appointment_type/appointment_type.json
+++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.json
@@ -12,7 +12,10 @@
   "appointment_type",
   "ip",
   "default_duration",
-  "color"
+  "color",
+  "billing_section",
+  "price_list",
+  "items"
  ],
  "fields": [
   {
@@ -52,10 +55,27 @@
    "label": "Color",
    "no_copy": 1,
    "report_hide": 1
+  },
+  {
+   "fieldname": "billing_section",
+   "fieldtype": "Section Break",
+   "label": "Billing"
+  },
+  {
+   "fieldname": "price_list",
+   "fieldtype": "Link",
+   "label": "Price List",
+   "options": "Price List"
+  },
+  {
+   "fieldname": "items",
+   "fieldtype": "Table",
+   "label": "Appointment Type Service Items",
+   "options": "Appointment Type Service Item"
   }
  ],
  "links": [],
- "modified": "2020-02-03 21:06:05.833050",
+ "modified": "2021-01-22 09:41:05.010524",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Appointment Type",
diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.py b/erpnext/healthcare/doctype/appointment_type/appointment_type.py
index 1dacffa..67a24f3 100644
--- a/erpnext/healthcare/doctype/appointment_type/appointment_type.py
+++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.py
@@ -4,6 +4,53 @@
 
 from __future__ import unicode_literals
 from frappe.model.document import Document
+import frappe
 
 class AppointmentType(Document):
-	pass
+	def validate(self):
+		if self.items and self.price_list:
+			for item in self.items:
+				existing_op_item_price = frappe.db.exists('Item Price', {
+					'item_code': item.op_consulting_charge_item,
+					'price_list': self.price_list
+				})
+
+				if not existing_op_item_price and item.op_consulting_charge_item and item.op_consulting_charge:
+					make_item_price(self.price_list, item.op_consulting_charge_item, item.op_consulting_charge)
+
+				existing_ip_item_price = frappe.db.exists('Item Price', {
+					'item_code': item.inpatient_visit_charge_item,
+					'price_list': self.price_list
+				})
+
+				if not existing_ip_item_price and item.inpatient_visit_charge_item and item.inpatient_visit_charge:
+					make_item_price(self.price_list, item.inpatient_visit_charge_item, item.inpatient_visit_charge)
+
+@frappe.whitelist()
+def get_service_item_based_on_department(appointment_type, department):
+	item_list = frappe.db.get_value('Appointment Type Service Item',
+		filters = {'medical_department': department, 'parent': appointment_type},
+		fieldname = ['op_consulting_charge_item',
+			'inpatient_visit_charge_item', 'op_consulting_charge', 'inpatient_visit_charge'],
+		as_dict = 1
+	)
+
+	# if department wise items are not set up
+	# use the generic items
+	if not item_list:
+		item_list = frappe.db.get_value('Appointment Type Service Item',
+			filters = {'parent': appointment_type},
+			fieldname = ['op_consulting_charge_item',
+				'inpatient_visit_charge_item', 'op_consulting_charge', 'inpatient_visit_charge'],
+			as_dict = 1
+		)
+
+	return item_list
+
+def make_item_price(price_list, item, item_price):
+	frappe.get_doc({
+		'doctype': 'Item Price',
+		'price_list': price_list,
+		'item_code': item,
+		'price_list_rate': item_price
+	}).insert(ignore_permissions=True, ignore_mandatory=True)
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/healthcare/doctype/appointment_type_service_item/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/healthcare/doctype/appointment_type_service_item/__init__.py
diff --git a/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json
new file mode 100644
index 0000000..5ff68cd
--- /dev/null
+++ b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json
@@ -0,0 +1,67 @@
+{
+ "actions": [],
+ "creation": "2021-01-22 09:34:53.373105",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "medical_department",
+  "op_consulting_charge_item",
+  "op_consulting_charge",
+  "column_break_4",
+  "inpatient_visit_charge_item",
+  "inpatient_visit_charge"
+ ],
+ "fields": [
+  {
+   "fieldname": "medical_department",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Medical Department",
+   "options": "Medical Department"
+  },
+  {
+   "fieldname": "op_consulting_charge_item",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Out Patient Consulting Charge Item",
+   "options": "Item"
+  },
+  {
+   "fieldname": "op_consulting_charge",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Out Patient Consulting Charge"
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "inpatient_visit_charge_item",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Inpatient Visit Charge Item",
+   "options": "Item"
+  },
+  {
+   "fieldname": "inpatient_visit_charge",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Inpatient Visit Charge Item"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-01-22 09:35:26.503443",
+ "modified_by": "Administrator",
+ "module": "Healthcare",
+ "name": "Appointment Type Service Item",
+ "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/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py
new file mode 100644
index 0000000..b2e0e82
--- /dev/null
+++ b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class AppointmentTypeServiceItem(Document):
+	pass
diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py
index e55a143..325c209 100644
--- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py
+++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py
@@ -100,7 +100,6 @@
 		allow_start = self.set_actual_qty()
 		if allow_start:
 			self.db_set('status', 'In Progress')
-			insert_clinical_procedure_to_medical_record(self)
 			return 'success'
 		return 'insufficient stock'
 
@@ -122,6 +121,7 @@
 
 		stock_entry.stock_entry_type = 'Material Receipt'
 		stock_entry.to_warehouse = self.warehouse
+		stock_entry.company = self.company
 		expense_account = get_account(None, 'expense_account', 'Healthcare Settings', self.company)
 		for item in self.items:
 			if item.qty > item.actual_qty:
@@ -247,21 +247,3 @@
 		}, target_doc, set_missing_values)
 
 	return doc
-
-
-def insert_clinical_procedure_to_medical_record(doc):
-	subject = frappe.bold(_("Clinical Procedure conducted: ")) + cstr(doc.procedure_template) + "<br>"
-	if doc.practitioner:
-		subject += frappe.bold(_('Healthcare Practitioner: ')) + doc.practitioner
-	if subject and doc.notes:
-		subject += '<br/>' + doc.notes
-
-	medical_record = frappe.new_doc('Patient Medical Record')
-	medical_record.patient = doc.patient
-	medical_record.subject = subject
-	medical_record.status = 'Open'
-	medical_record.communication_date = doc.start_date
-	medical_record.reference_doctype = 'Clinical Procedure'
-	medical_record.reference_name = doc.name
-	medical_record.reference_owner = doc.owner
-	medical_record.save(ignore_permissions=True)
diff --git a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py
index 4ee5f6b..fb72073 100644
--- a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py
+++ b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py
@@ -1,4 +1,4 @@
-# -*- coding: utf-8 -*-
+	# -*- coding: utf-8 -*-
 # Copyright (c) 2017, ESS LLP and Contributors
 # See license.txt
 from __future__ import unicode_literals
@@ -60,6 +60,7 @@
 	procedure.practitioner = practitioner
 	procedure.consume_stock = procedure_template.allow_stock_consumption
 	procedure.items = procedure_template.items
-	procedure.warehouse = frappe.db.get_single_value('Stock Settings', 'default_warehouse')
+	procedure.company = "_Test Company"
+	procedure.warehouse = "_Test Warehouse - _TC"
 	procedure.submit()
 	return procedure
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json
index cb747f9..8162f03 100644
--- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json
+++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json
@@ -159,6 +159,7 @@
    "fieldname": "op_consulting_charge",
    "fieldtype": "Currency",
    "label": "Out Patient Consulting Charge",
+   "mandatory_depends_on": "op_consulting_charge_item",
    "options": "Currency"
   },
   {
@@ -174,7 +175,8 @@
   {
    "fieldname": "inpatient_visit_charge",
    "fieldtype": "Currency",
-   "label": "Inpatient Visit Charge"
+   "label": "Inpatient Visit Charge",
+   "mandatory_depends_on": "inpatient_visit_charge_item"
   },
   {
    "depends_on": "eval: !doc.__islocal",
@@ -280,7 +282,7 @@
  ],
  "image_field": "image",
  "links": [],
- "modified": "2020-04-06 13:44:24.759623",
+ "modified": "2021-01-22 10:14:43.187675",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Healthcare Practitioner",
diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json
index 0104386..ddf1bce 100644
--- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json
+++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json
@@ -17,6 +17,9 @@
   "enable_free_follow_ups",
   "max_visits",
   "valid_days",
+  "inpatient_settings_section",
+  "allow_discharge_despite_unbilled_services",
+  "do_not_bill_inpatient_encounters",
   "healthcare_service_items",
   "inpatient_visit_charge_item",
   "op_consulting_charge_item",
@@ -302,11 +305,28 @@
    "fieldname": "enable_free_follow_ups",
    "fieldtype": "Check",
    "label": "Enable Free Follow-ups"
+  },
+  {
+   "fieldname": "inpatient_settings_section",
+   "fieldtype": "Section Break",
+   "label": "Inpatient Settings"
+  },
+  {
+   "default": "0",
+   "fieldname": "allow_discharge_despite_unbilled_services",
+   "fieldtype": "Check",
+   "label": "Allow Discharge Despite Unbilled Healthcare Services"
+  },
+  {
+   "default": "0",
+   "fieldname": "do_not_bill_inpatient_encounters",
+   "fieldtype": "Check",
+   "label": "Do Not Bill Patient Encounters for Inpatients"
   }
  ],
  "issingle": 1,
  "links": [],
- "modified": "2020-07-08 15:17:21.543218",
+ "modified": "2021-01-13 09:04:35.877700",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Healthcare Settings",
diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js
index ca97489..a7b06b1 100644
--- a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js
+++ b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js
@@ -5,6 +5,7 @@
 	refresh: function(frm) {
 		// Ignore cancellation of doctype on cancel all
 		frm.ignore_doctypes_on_cancel_all = ['Stock Entry'];
+		frm.fields_dict['medication_orders'].grid.wrapper.find('.grid-add-row').hide();
 
 		frm.set_query('item_code', () => {
 			return {
diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.json b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.json
index dd4c423..b1a6ee4 100644
--- a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.json
+++ b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.json
@@ -139,7 +139,6 @@
    "fieldtype": "Table",
    "label": "Inpatient Medication Orders",
    "options": "Inpatient Medication Entry Detail",
-   "read_only": 1,
    "reqd": 1
   },
   {
@@ -180,7 +179,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-11-03 13:22:37.820707",
+ "modified": "2021-01-11 12:37:46.749659",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Inpatient Medication Entry",
diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py
index 70ae713..e731908 100644
--- a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py
+++ b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py
@@ -15,8 +15,6 @@
 		self.validate_medication_orders()
 
 	def get_medication_orders(self):
-		self.validate_datetime_filters()
-
 		# pull inpatient medication orders based on selected filters
 		orders = get_pending_medication_orders(self)
 
@@ -27,22 +25,6 @@
 			self.set('medication_orders', [])
 			frappe.msgprint(_('No pending medication orders found for selected criteria'))
 
-	def validate_datetime_filters(self):
-		if self.from_date and self.to_date:
-			self.validate_from_to_dates('from_date', 'to_date')
-
-		if self.from_date and getdate(self.from_date) > getdate():
-			frappe.throw(_('From Date cannot be after the current date.'))
-
-		if self.to_date and getdate(self.to_date) > getdate():
-			frappe.throw(_('To Date cannot be after the current date.'))
-
-		if self.from_time and self.from_time > nowtime():
-			frappe.throw(_('From Time cannot be after the current time.'))
-
-		if self.to_time and self.to_time > nowtime():
-			frappe.throw(_('To Time cannot be after the current time.'))
-
 	def add_mo_to_table(self, orders):
 		# Add medication orders in the child table
 		self.set('medication_orders', [])
@@ -282,7 +264,7 @@
 
 def get_current_healthcare_service_unit(inpatient_record):
 	ip_record = frappe.get_doc('Inpatient Record', inpatient_record)
-	if ip_record.inpatient_occupancies:
+	if ip_record.status in ['Admitted', 'Discharge Scheduled'] and ip_record.inpatient_occupancies:
 		return ip_record.inpatient_occupancies[-1].service_unit
 	return
 
diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py
index bc76970..dc549a6 100644
--- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py
+++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py
@@ -5,7 +5,7 @@
 from __future__ import unicode_literals
 import frappe, json
 from frappe import _
-from frappe.utils import today, now_datetime, getdate, get_datetime
+from frappe.utils import today, now_datetime, getdate, get_datetime, get_link_to_form
 from frappe.model.document import Document
 from frappe.desk.reportview import get_match_cond
 
@@ -113,6 +113,7 @@
 	inpatient_record.status = 'Admission Scheduled'
 	inpatient_record.save(ignore_permissions = True)
 
+
 @frappe.whitelist()
 def schedule_discharge(args):
 	discharge_order = json.loads(args)
@@ -126,16 +127,19 @@
 		frappe.db.set_value('Patient', discharge_order['patient'], 'inpatient_status', inpatient_record.status)
 		frappe.db.set_value('Patient Encounter', inpatient_record.discharge_encounter, 'inpatient_status', inpatient_record.status)
 
+
 def set_details_from_ip_order(inpatient_record, ip_order):
 	for key in ip_order:
 		inpatient_record.set(key, ip_order[key])
 
+
 def set_ip_child_records(inpatient_record, inpatient_record_child, encounter_child):
 	for item in encounter_child:
 		table = inpatient_record.append(inpatient_record_child)
 		for df in table.meta.get('fields'):
 			table.set(df.fieldname, item.get(df.fieldname))
 
+
 def check_out_inpatient(inpatient_record):
 	if inpatient_record.inpatient_occupancies:
 		for inpatient_occupancy in inpatient_record.inpatient_occupancies:
@@ -144,54 +148,88 @@
 				inpatient_occupancy.check_out = now_datetime()
 				frappe.db.set_value("Healthcare Service Unit", inpatient_occupancy.service_unit, "occupancy_status", "Vacant")
 
+
 def discharge_patient(inpatient_record):
-	validate_invoiced_inpatient(inpatient_record)
+	validate_inpatient_invoicing(inpatient_record)
 	inpatient_record.discharge_date = today()
 	inpatient_record.status = "Discharged"
 
 	inpatient_record.save(ignore_permissions = True)
 
-def validate_invoiced_inpatient(inpatient_record):
-	pending_invoices = []
+
+def validate_inpatient_invoicing(inpatient_record):
+	if frappe.db.get_single_value("Healthcare Settings", "allow_discharge_despite_unbilled_services"):
+		return
+
+	pending_invoices = get_pending_invoices(inpatient_record)
+
+	if pending_invoices:
+		message = _("Cannot mark Inpatient Record as Discharged since there are unbilled services. ")
+
+		formatted_doc_rows = ''
+
+		for doctype, docnames in pending_invoices.items():
+			formatted_doc_rows += """
+				<td>{0}</td>
+				<td>{1}</td>
+			</tr>""".format(doctype, docnames)
+
+		message += """
+			<table class='table'>
+				<thead>
+					<th>{0}</th>
+					<th>{1}</th>
+				</thead>
+				{2}
+			</table>
+		""".format(_("Healthcare Service"), _("Documents"), formatted_doc_rows)
+
+		frappe.throw(message, title=_("Unbilled Services"), is_minimizable=True, wide=True)
+
+
+def get_pending_invoices(inpatient_record):
+	pending_invoices = {}
 	if inpatient_record.inpatient_occupancies:
 		service_unit_names = False
 		for inpatient_occupancy in inpatient_record.inpatient_occupancies:
-			if inpatient_occupancy.invoiced != 1:
+			if not inpatient_occupancy.invoiced:
 				if service_unit_names:
 					service_unit_names += ", " + inpatient_occupancy.service_unit
 				else:
 					service_unit_names = inpatient_occupancy.service_unit
 		if service_unit_names:
-			pending_invoices.append("Inpatient Occupancy (" + service_unit_names + ")")
+			pending_invoices["Inpatient Occupancy"] = service_unit_names
 
 	docs = ["Patient Appointment", "Patient Encounter", "Lab Test", "Clinical Procedure"]
 
 	for doc in docs:
-		doc_name_list = get_inpatient_docs_not_invoiced(doc, inpatient_record)
+		doc_name_list = get_unbilled_inpatient_docs(doc, inpatient_record)
 		if doc_name_list:
 			pending_invoices = get_pending_doc(doc, doc_name_list, pending_invoices)
 
-	if pending_invoices:
-		frappe.throw(_("Can not mark Inpatient Record Discharged, there are Unbilled Invoices {0}").format(", "
-			.join(pending_invoices)), title=_('Unbilled Invoices'))
+	return pending_invoices
+
 
 def get_pending_doc(doc, doc_name_list, pending_invoices):
 	if doc_name_list:
 		doc_ids = False
 		for doc_name in doc_name_list:
+			doc_link = get_link_to_form(doc, doc_name.name)
 			if doc_ids:
-				doc_ids += ", "+doc_name.name
+				doc_ids += ", " + doc_link
 			else:
-				doc_ids = doc_name.name
+				doc_ids = doc_link
 		if doc_ids:
-			pending_invoices.append(doc + " (" + doc_ids + ")")
+			pending_invoices[doc] =  doc_ids
 
 	return pending_invoices
 
-def get_inpatient_docs_not_invoiced(doc, inpatient_record):
+
+def get_unbilled_inpatient_docs(doc, inpatient_record):
 	return frappe.db.get_list(doc, filters = {'patient': inpatient_record.patient,
 					'inpatient_record': inpatient_record.name, 'docstatus': 1, 'invoiced': 0})
 
+
 def admit_patient(inpatient_record, service_unit, check_in, expected_discharge=None):
 	inpatient_record.admitted_datetime = check_in
 	inpatient_record.status = 'Admitted'
@@ -203,6 +241,7 @@
 	frappe.db.set_value('Patient', inpatient_record.patient, 'inpatient_status', 'Admitted')
 	frappe.db.set_value('Patient', inpatient_record.patient, 'inpatient_record', inpatient_record.name)
 
+
 def transfer_patient(inpatient_record, service_unit, check_in):
 	item_line = inpatient_record.append('inpatient_occupancies', {})
 	item_line.service_unit = service_unit
@@ -212,6 +251,7 @@
 
 	frappe.db.set_value("Healthcare Service Unit", service_unit, "occupancy_status", "Occupied")
 
+
 def patient_leave_service_unit(inpatient_record, check_out, leave_from):
 	if inpatient_record.inpatient_occupancies:
 		for inpatient_occupancy in inpatient_record.inpatient_occupancies:
@@ -221,6 +261,7 @@
 				frappe.db.set_value("Healthcare Service Unit", inpatient_occupancy.service_unit, "occupancy_status", "Vacant")
 	inpatient_record.save(ignore_permissions = True)
 
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_leave_from(doctype, txt, searchfield, start, page_len, filters):
diff --git a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py
index 70706ad..ea0d1e9 100644
--- a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py
+++ b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py
@@ -8,6 +8,8 @@
 from frappe.utils import now_datetime, today
 from frappe.utils.make_random import get_random
 from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge
+from erpnext.healthcare.doctype.lab_test.test_lab_test import create_patient_encounter
+from erpnext.healthcare.utils import get_encounters_to_invoice
 
 class TestInpatientRecord(unittest.TestCase):
 	def test_admit_and_discharge(self):
@@ -40,6 +42,60 @@
 		self.assertEqual(None, frappe.db.get_value("Patient", patient, "inpatient_record"))
 		self.assertEqual(None, frappe.db.get_value("Patient", patient, "inpatient_status"))
 
+	def test_allow_discharge_despite_unbilled_services(self):
+		frappe.db.sql("""delete from `tabInpatient Record`""")
+		setup_inpatient_settings(key="allow_discharge_despite_unbilled_services", value=1)
+		patient = create_patient()
+		# Schedule Admission
+		ip_record = create_inpatient(patient)
+		ip_record.expected_length_of_stay = 0
+		ip_record.save(ignore_permissions = True)
+
+		# Admit
+		service_unit = get_healthcare_service_unit()
+		admit_patient(ip_record, service_unit, now_datetime())
+
+		# Discharge
+		schedule_discharge(frappe.as_json({"patient": patient}))
+		self.assertEqual("Vacant", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status"))
+
+		ip_record = frappe.get_doc("Inpatient Record", ip_record.name)
+		# Should not validate Pending Invoices
+		ip_record.discharge()
+
+		self.assertEqual(None, frappe.db.get_value("Patient", patient, "inpatient_record"))
+		self.assertEqual(None, frappe.db.get_value("Patient", patient, "inpatient_status"))
+
+		setup_inpatient_settings(key="allow_discharge_despite_unbilled_services", value=0)
+
+	def test_do_not_bill_patient_encounters_for_inpatients(self):
+		frappe.db.sql("""delete from `tabInpatient Record`""")
+		setup_inpatient_settings(key="do_not_bill_inpatient_encounters", value=1)
+		patient = create_patient()
+		# Schedule Admission
+		ip_record = create_inpatient(patient)
+		ip_record.expected_length_of_stay = 0
+		ip_record.save(ignore_permissions = True)
+
+		# Admit
+		service_unit = get_healthcare_service_unit()
+		admit_patient(ip_record, service_unit, now_datetime())
+
+		# Patient Encounter
+		patient_encounter = create_patient_encounter()
+		encounters = get_encounters_to_invoice(patient, "_Test Company")
+		encounter_ids = [entry.reference_name for entry in encounters]
+		self.assertFalse(patient_encounter.name in encounter_ids)
+
+		# Discharge
+		schedule_discharge(frappe.as_json({"patient": patient}))
+		self.assertEqual("Vacant", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status"))
+
+		ip_record = frappe.get_doc("Inpatient Record", ip_record.name)
+		mark_invoiced_inpatient_occupancy(ip_record)
+		discharge_patient(ip_record)
+		setup_inpatient_settings(key="do_not_bill_inpatient_encounters", value=0)
+
 	def test_validate_overlap_admission(self):
 		frappe.db.sql("""delete from `tabInpatient Record`""")
 		patient = create_patient()
@@ -63,6 +119,13 @@
 			inpatient_occupancy.invoiced = 1
 		ip_record.save(ignore_permissions = True)
 
+
+def setup_inpatient_settings(key, value):
+	settings = frappe.get_single("Healthcare Settings")
+	settings.set(key, value)
+	settings.save()
+
+
 def create_inpatient(patient):
 	patient_obj = frappe.get_doc('Patient', patient)
 	inpatient_record = frappe.new_doc('Inpatient Record')
@@ -76,13 +139,19 @@
 	inpatient_record.phone = patient_obj.phone
 	inpatient_record.inpatient = "Scheduled"
 	inpatient_record.scheduled_date = today()
+	inpatient_record.company = "_Test Company"
 	return inpatient_record
 
-def get_healthcare_service_unit():
-	service_unit = get_random("Healthcare Service Unit", filters={"inpatient_occupancy": 1})
+
+def get_healthcare_service_unit(unit_name=None):
+	if not unit_name:
+		service_unit = get_random("Healthcare Service Unit", filters={"inpatient_occupancy": 1})
+	else:
+		service_unit = frappe.db.exists("Healthcare Service Unit", {"healthcare_service_unit_name": unit_name})
+
 	if not service_unit:
 		service_unit = frappe.new_doc("Healthcare Service Unit")
-		service_unit.healthcare_service_unit_name = "Test Service Unit Ip Occupancy"
+		service_unit.healthcare_service_unit_name = unit_name or "Test Service Unit Ip Occupancy"
 		service_unit.company = "_Test Company"
 		service_unit.service_unit_type = get_service_unit_type()
 		service_unit.inpatient_occupancy = 1
@@ -105,6 +174,7 @@
 		return service_unit.name
 	return service_unit
 
+
 def get_service_unit_type():
 	service_unit_type = get_random("Healthcare Service Unit Type", filters={"inpatient_occupancy": 1})
 
@@ -116,6 +186,7 @@
 		return service_unit_type.name
 	return service_unit_type
 
+
 def create_patient():
 	patient = frappe.db.exists('Patient', '_Test IPD Patient')
 	if not patient:
diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.json b/erpnext/healthcare/doctype/lab_test/lab_test.json
index edf1d91..ac61fea 100644
--- a/erpnext/healthcare/doctype/lab_test/lab_test.json
+++ b/erpnext/healthcare/doctype/lab_test/lab_test.json
@@ -359,6 +359,7 @@
   {
    "fieldname": "normal_test_items",
    "fieldtype": "Table",
+   "label": "Normal Test Result",
    "options": "Normal Test Result",
    "print_hide": 1
   },
@@ -380,6 +381,7 @@
   {
    "fieldname": "sensitivity_test_items",
    "fieldtype": "Table",
+   "label": "Sensitivity Test Result",
    "options": "Sensitivity Test Result",
    "print_hide": 1,
    "report_hide": 1
@@ -529,6 +531,7 @@
   {
    "fieldname": "descriptive_test_items",
    "fieldtype": "Table",
+   "label": "Descriptive Test Result",
    "options": "Descriptive Test Result",
    "print_hide": 1,
    "report_hide": 1
@@ -549,13 +552,14 @@
   {
    "fieldname": "organism_test_items",
    "fieldtype": "Table",
+   "label": "Organism Test Result",
    "options": "Organism Test Result",
    "print_hide": 1
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2020-07-30 18:18:38.516215",
+ "modified": "2020-11-30 11:04:17.195848",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Lab Test",
diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py
index 2db7743..4b57cd0 100644
--- a/erpnext/healthcare/doctype/lab_test/lab_test.py
+++ b/erpnext/healthcare/doctype/lab_test/lab_test.py
@@ -17,11 +17,9 @@
 		self.validate_result_values()
 		self.db_set('submitted_date', getdate())
 		self.db_set('status', 'Completed')
-		insert_lab_test_to_medical_record(self)
 
 	def on_cancel(self):
 		self.db_set('status', 'Cancelled')
-		delete_lab_test_from_medical_record(self)
 		self.reload()
 
 	def on_update(self):
@@ -330,60 +328,6 @@
 		return frappe.get_doc('Employee', emp_id)
 	return None
 
-def insert_lab_test_to_medical_record(doc):
-	table_row = False
-	subject = cstr(doc.lab_test_name)
-	if doc.practitioner:
-		subject += frappe.bold(_('Healthcare Practitioner: '))+ doc.practitioner + '<br>'
-	if doc.normal_test_items:
-		item = doc.normal_test_items[0]
-		comment = ''
-		if item.lab_test_comment:
-			comment = str(item.lab_test_comment)
-		table_row = frappe.bold(_('Lab Test Conducted: ')) + item.lab_test_name
-
-		if item.lab_test_event:
-			table_row += frappe.bold(_('Lab Test Event: ')) + item.lab_test_event
-
-		if item.result_value:
-			table_row += ' ' + frappe.bold(_('Lab Test Result: ')) + item.result_value
-
-		if item.normal_range:
-			table_row += ' ' + _('Normal Range: ') + item.normal_range
-		table_row += ' ' + comment
-
-	elif doc.descriptive_test_items:
-		item = doc.descriptive_test_items[0]
-
-		if item.lab_test_particulars and item.result_value:
-			table_row = item.lab_test_particulars + ' ' + item.result_value
-
-	elif doc.sensitivity_test_items:
-		item = doc.sensitivity_test_items[0]
-
-		if item.antibiotic and item.antibiotic_sensitivity:
-			table_row = item.antibiotic + ' ' + item.antibiotic_sensitivity
-
-	if table_row:
-		subject += '<br>' + table_row
-	if doc.lab_test_comment:
-		subject += '<br>' + cstr(doc.lab_test_comment)
-
-	medical_record = frappe.new_doc('Patient Medical Record')
-	medical_record.patient = doc.patient
-	medical_record.subject = subject
-	medical_record.status = 'Open'
-	medical_record.communication_date = doc.result_date
-	medical_record.reference_doctype = 'Lab Test'
-	medical_record.reference_name = doc.name
-	medical_record.reference_owner = doc.owner
-	medical_record.save(ignore_permissions = True)
-
-def delete_lab_test_from_medical_record(self):
-	medical_record_id = frappe.db.sql('select name from `tabPatient Medical Record` where reference_name=%s', (self.name))
-
-	if medical_record_id and medical_record_id[0][0]:
-		frappe.delete_doc('Patient Medical Record', medical_record_id[0][0])
 
 @frappe.whitelist()
 def get_lab_test_prescribed(patient):
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
index 2d6b645..0354733 100644
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
@@ -22,23 +22,37 @@
 				filters: {'status': 'Active'}
 			};
 		});
+
 		frm.set_query('practitioner', function() {
+			if (frm.doc.department) {
+				return {
+					filters: {
+						'department': frm.doc.department
+					}
+				};
+			}
+		});
+
+		frm.set_query('service_unit', function() {
 			return {
+				query: 'erpnext.controllers.queries.get_healthcare_service_units',
 				filters: {
-					'department': frm.doc.department
+					company: frm.doc.company,
+					inpatient_record: frm.doc.inpatient_record
 				}
 			};
 		});
-		frm.set_query('service_unit', function(){
+
+		frm.set_query('therapy_plan', function() {
 			return {
 				filters: {
-					'is_group': false,
-					'allow_appointments': true,
-					'company': frm.doc.company
+					'patient': frm.doc.patient
 				}
 			};
 		});
 
+		frm.trigger('set_therapy_type_filter');
+
 		if (frm.is_new()) {
 			frm.page.set_primary_action(__('Check Availability'), function() {
 				if (!frm.doc.patient) {
@@ -128,6 +142,20 @@
 	patient: function(frm) {
 		if (frm.doc.patient) {
 			frm.trigger('toggle_payment_fields');
+			frappe.call({
+				method: 'frappe.client.get',
+				args: {
+					doctype: 'Patient',
+					name: frm.doc.patient
+				},
+				callback: function (data) {
+					let age = null;
+					if (data.message.dob) {
+						age = calculate_age(data.message.dob);
+					}
+					frappe.model.set_value(frm.doctype, frm.docname, 'patient_age', age);
+				}
+			});
 		} else {
 			frm.set_value('patient_name', '');
 			frm.set_value('patient_sex', '');
@@ -136,6 +164,55 @@
 		}
 	},
 
+	practitioner: function(frm) {
+		if (frm.doc.practitioner ) {
+			frm.events.set_payment_details(frm);
+		}
+	},
+
+	appointment_type: function(frm) {
+		if (frm.doc.appointment_type) {
+			frm.events.set_payment_details(frm);
+		}
+	},
+
+	set_payment_details: function(frm) {
+		frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing').then(val => {
+			if (val) {
+				frappe.call({
+					method: 'erpnext.healthcare.utils.get_service_item_and_practitioner_charge',
+					args: {
+						doc: frm.doc
+					},
+					callback: function(data) {
+						if (data.message) {
+							frappe.model.set_value(frm.doctype, frm.docname, 'paid_amount', data.message.practitioner_charge);
+							frappe.model.set_value(frm.doctype, frm.docname, 'billing_item', data.message.service_item);
+						}
+					}
+				});
+			}
+		});
+	},
+
+	therapy_plan: function(frm) {
+		frm.trigger('set_therapy_type_filter');
+	},
+
+	set_therapy_type_filter: function(frm) {
+		if (frm.doc.therapy_plan) {
+			frm.call('get_therapy_types').then(r => {
+				frm.set_query('therapy_type', function() {
+					return {
+						filters: {
+							'name': ['in', r.message]
+						}
+					};
+				});
+			});
+		}
+	},
+
 	therapy_type: function(frm) {
 		if (frm.doc.therapy_type) {
 			frappe.db.get_value('Therapy Type', frm.doc.therapy_type, 'default_duration', (r) => {
@@ -160,14 +237,18 @@
 					// show payment fields as non-mandatory
 					frm.toggle_display('mode_of_payment', 0);
 					frm.toggle_display('paid_amount', 0);
+					frm.toggle_display('billing_item', 0);
 					frm.toggle_reqd('mode_of_payment', 0);
 					frm.toggle_reqd('paid_amount', 0);
+					frm.toggle_reqd('billing_item', 0);
 				} else {
 					// if automated appointment invoicing is disabled, hide fields
 					frm.toggle_display('mode_of_payment', data.message ? 1 : 0);
 					frm.toggle_display('paid_amount', data.message ? 1 : 0);
+					frm.toggle_display('billing_item', data.message ? 1 : 0);
 					frm.toggle_reqd('mode_of_payment', data.message ? 1 : 0);
 					frm.toggle_reqd('paid_amount', data.message ? 1 :0);
+					frm.toggle_reqd('billing_item', data.message ? 1 : 0);
 				}
 			}
 		});
@@ -510,57 +591,6 @@
 	);
 };
 
-frappe.ui.form.on('Patient Appointment', 'practitioner', function(frm) {
-	if (frm.doc.practitioner) {
-		frappe.call({
-			method: 'frappe.client.get',
-			args: {
-				doctype: 'Healthcare Practitioner',
-				name: frm.doc.practitioner
-			},
-			callback: function (data) {
-				frappe.model.set_value(frm.doctype, frm.docname, 'department', data.message.department);
-				frappe.model.set_value(frm.doctype, frm.docname, 'paid_amount', data.message.op_consulting_charge);
-				frappe.model.set_value(frm.doctype, frm.docname, 'billing_item', data.message.op_consulting_charge_item);
-			}
-		});
-	}
-});
-
-frappe.ui.form.on('Patient Appointment', 'patient', function(frm) {
-	if (frm.doc.patient) {
-		frappe.call({
-			method: 'frappe.client.get',
-			args: {
-				doctype: 'Patient',
-				name: frm.doc.patient
-			},
-			callback: function (data) {
-				let age = null;
-				if (data.message.dob) {
-					age = calculate_age(data.message.dob);
-				}
-				frappe.model.set_value(frm.doctype,frm.docname, 'patient_age', age);
-			}
-		});
-	}
-});
-
-frappe.ui.form.on('Patient Appointment', 'appointment_type', function(frm) {
-	if (frm.doc.appointment_type) {
-		frappe.call({
-			method: 'frappe.client.get',
-			args: {
-				doctype: 'Appointment Type',
-				name: frm.doc.appointment_type
-			},
-			callback: function(data) {
-				frappe.model.set_value(frm.doctype,frm.docname, 'duration',data.message.default_duration);
-			}
-		});
-	}
-});
-
 let calculate_age = function(birth) {
 	let ageMS = Date.parse(Date()) - Date.parse(birth);
 	let age = new Date();
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
index ac35acc..83c92af 100644
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
@@ -19,19 +19,19 @@
   "inpatient_record",
   "column_break_1",
   "company",
-  "service_unit",
-  "procedure_template",
-  "get_procedure_from_encounter",
-  "procedure_prescription",
-  "therapy_type",
-  "get_prescribed_therapies",
-  "therapy_plan",
   "practitioner",
   "practitioner_name",
   "department",
+  "service_unit",
   "section_break_12",
   "appointment_type",
   "duration",
+  "procedure_template",
+  "get_procedure_from_encounter",
+  "procedure_prescription",
+  "therapy_plan",
+  "therapy_type",
+  "get_prescribed_therapies",
   "column_break_17",
   "appointment_date",
   "appointment_time",
@@ -79,6 +79,7 @@
    "set_only_once": 1
   },
   {
+   "fetch_from": "appointment_type.default_duration",
    "fieldname": "duration",
    "fieldtype": "Int",
    "in_filter": 1,
@@ -144,7 +145,6 @@
    "in_standard_filter": 1,
    "label": "Healthcare Practitioner",
    "options": "Healthcare Practitioner",
-   "read_only": 1,
    "reqd": 1,
    "search_index": 1,
    "set_only_once": 1
@@ -158,7 +158,6 @@
    "in_standard_filter": 1,
    "label": "Department",
    "options": "Medical Department",
-   "read_only": 1,
    "search_index": 1,
    "set_only_once": 1
   },
@@ -227,12 +226,14 @@
    "fieldname": "mode_of_payment",
    "fieldtype": "Link",
    "label": "Mode of Payment",
-   "options": "Mode of Payment"
+   "options": "Mode of Payment",
+   "read_only_depends_on": "invoiced"
   },
   {
    "fieldname": "paid_amount",
    "fieldtype": "Currency",
-   "label": "Paid Amount"
+   "label": "Paid Amount",
+   "read_only_depends_on": "invoiced"
   },
   {
    "fieldname": "column_break_2",
@@ -284,7 +285,7 @@
    "report_hide": 1
   },
   {
-   "depends_on": "eval:doc.patient;",
+   "depends_on": "eval:doc.patient && doc.therapy_plan;",
    "fieldname": "therapy_type",
    "fieldtype": "Link",
    "label": "Therapy",
@@ -292,18 +293,18 @@
    "set_only_once": 1
   },
   {
-   "depends_on": "eval:doc.patient && doc.__islocal;",
+   "depends_on": "eval:doc.patient && doc.therapy_plan && doc.__islocal;",
    "fieldname": "get_prescribed_therapies",
    "fieldtype": "Button",
    "label": "Get Prescribed Therapies"
   },
   {
-   "depends_on": "eval: doc.patient && doc.therapy_type",
+   "depends_on": "eval: doc.patient;",
    "fieldname": "therapy_plan",
    "fieldtype": "Link",
    "label": "Therapy Plan",
-   "mandatory_depends_on": "eval: doc.patient && doc.therapy_type",
-   "options": "Therapy Plan"
+   "options": "Therapy Plan",
+   "set_only_once": 1
   },
   {
    "fieldname": "ref_sales_invoice",
@@ -348,7 +349,7 @@
   }
  ],
  "links": [],
- "modified": "2020-05-21 03:04:21.400893",
+ "modified": "2021-02-08 13:13:15.116833",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Patient Appointment",
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
index e685b20..649f16d 100755
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
@@ -18,6 +18,7 @@
 class PatientAppointment(Document):
 	def validate(self):
 		self.validate_overlaps()
+		self.validate_service_unit()
 		self.set_appointment_datetime()
 		self.validate_customer_created()
 		self.set_status()
@@ -25,6 +26,7 @@
 
 	def after_insert(self):
 		self.update_prescription_details()
+		self.set_payment_details()
 		invoice_appointment(self)
 		self.update_fee_validity()
 		send_confirmation_msg(self)
@@ -68,9 +70,29 @@
 				overlaps[0][1], overlaps[0][2], overlaps[0][3], overlaps[0][4])
 			frappe.throw(overlapping_details, title=_('Appointments Overlapping'))
 
+	def validate_service_unit(self):
+		if self.inpatient_record and self.service_unit:
+			from erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry import get_current_healthcare_service_unit
+
+			is_inpatient_occupancy_unit = frappe.db.get_value('Healthcare Service Unit', self.service_unit,
+				'inpatient_occupancy')
+			service_unit = get_current_healthcare_service_unit(self.inpatient_record)
+			if is_inpatient_occupancy_unit and service_unit != self.service_unit:
+				msg = _('Patient {0} is not admitted in the service unit {1}').format(frappe.bold(self.patient), frappe.bold(self.service_unit)) + '<br>'
+				msg += _('Appointment for service units with Inpatient Occupancy can only be created against the unit where patient has been admitted.')
+				frappe.throw(msg, title=_('Invalid Healthcare Service Unit'))
+
+
 	def set_appointment_datetime(self):
 		self.appointment_datetime = "%s %s" % (self.appointment_date, self.appointment_time or "00:00:00")
 
+	def set_payment_details(self):
+		if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'):
+			details = get_service_item_and_practitioner_charge(self)
+			self.db_set('billing_item', details.get('service_item'))
+			if not self.paid_amount:
+				self.db_set('paid_amount', details.get('practitioner_charge'))
+
 	def validate_customer_created(self):
 		if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'):
 			if not frappe.db.get_value('Patient', self.patient, 'customer'):
@@ -91,6 +113,17 @@
 		if fee_validity:
 			frappe.msgprint(_('{0} has fee validity till {1}').format(self.patient, fee_validity.valid_till))
 
+	def get_therapy_types(self):
+		if not self.therapy_plan:
+			return
+
+		therapy_types = []
+		doc = frappe.get_doc('Therapy Plan', self.therapy_plan)
+		for entry in doc.therapy_plan_details:
+			therapy_types.append(entry.therapy_type)
+
+		return therapy_types
+
 
 @frappe.whitelist()
 def check_payment_fields_reqd(patient):
@@ -123,31 +156,37 @@
 		fee_validity = None
 
 	if automate_invoicing and not appointment_invoiced and not fee_validity:
-		sales_invoice = frappe.new_doc('Sales Invoice')
-		sales_invoice.patient = appointment_doc.patient
-		sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer')
-		sales_invoice.appointment = appointment_doc.name
-		sales_invoice.due_date = getdate()
-		sales_invoice.company = appointment_doc.company
-		sales_invoice.debit_to = get_receivable_account(appointment_doc.company)
+		create_sales_invoice(appointment_doc)
 
-		item = sales_invoice.append('items', {})
-		item = get_appointment_item(appointment_doc, item)
 
-		# Add payments if payment details are supplied else proceed to create invoice as Unpaid
-		if appointment_doc.mode_of_payment and appointment_doc.paid_amount:
-			sales_invoice.is_pos = 1
-			payment = sales_invoice.append('payments', {})
-			payment.mode_of_payment = appointment_doc.mode_of_payment
-			payment.amount = appointment_doc.paid_amount
+def create_sales_invoice(appointment_doc):
+	sales_invoice = frappe.new_doc('Sales Invoice')
+	sales_invoice.patient = appointment_doc.patient
+	sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer')
+	sales_invoice.appointment = appointment_doc.name
+	sales_invoice.due_date = getdate()
+	sales_invoice.company = appointment_doc.company
+	sales_invoice.debit_to = get_receivable_account(appointment_doc.company)
 
-		sales_invoice.set_missing_values(for_validate=True)
-		sales_invoice.flags.ignore_mandatory = True
-		sales_invoice.save(ignore_permissions=True)
-		sales_invoice.submit()
-		frappe.msgprint(_('Sales Invoice {0} created'.format(sales_invoice.name)), alert=True)
-		frappe.db.set_value('Patient Appointment', appointment_doc.name, 'invoiced', 1)
-		frappe.db.set_value('Patient Appointment', appointment_doc.name, 'ref_sales_invoice', sales_invoice.name)
+	item = sales_invoice.append('items', {})
+	item = get_appointment_item(appointment_doc, item)
+
+	# Add payments if payment details are supplied else proceed to create invoice as Unpaid
+	if appointment_doc.mode_of_payment and appointment_doc.paid_amount:
+		sales_invoice.is_pos = 1
+		payment = sales_invoice.append('payments', {})
+		payment.mode_of_payment = appointment_doc.mode_of_payment
+		payment.amount = appointment_doc.paid_amount
+
+	sales_invoice.set_missing_values(for_validate=True)
+	sales_invoice.flags.ignore_mandatory = True
+	sales_invoice.save(ignore_permissions=True)
+	sales_invoice.submit()
+	frappe.msgprint(_('Sales Invoice {0} created').format(sales_invoice.name), alert=True)
+	frappe.db.set_value('Patient Appointment', appointment_doc.name, {
+		'invoiced': 1,
+		'ref_sales_invoice': sales_invoice.name
+	})
 
 
 def check_is_new_patient(patient, name=None):
@@ -162,13 +201,14 @@
 
 
 def get_appointment_item(appointment_doc, item):
-	service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment_doc)
-	item.item_code = service_item
+	details = get_service_item_and_practitioner_charge(appointment_doc)
+	charge = appointment_doc.paid_amount or details.get('practitioner_charge')
+	item.item_code = details.get('service_item')
 	item.description = _('Consulting Charges: {0}').format(appointment_doc.practitioner)
 	item.income_account = get_income_account(appointment_doc.practitioner, appointment_doc.company)
 	item.cost_center = frappe.get_cached_value('Company', appointment_doc.company, 'cost_center')
-	item.rate = practitioner_charge
-	item.amount = practitioner_charge
+	item.rate = charge
+	item.amount = charge
 	item.qty = 1
 	item.reference_dt = 'Patient Appointment'
 	item.reference_dn = appointment_doc.name
diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
index 3df7ba1..2bb8a53 100644
--- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
+++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
@@ -5,7 +5,7 @@
 import unittest
 import frappe
 from erpnext.healthcare.doctype.patient_appointment.patient_appointment import update_status, make_encounter
-from frappe.utils import nowdate, add_days
+from frappe.utils import nowdate, add_days, now_datetime
 from frappe.utils.make_random import get_random
 from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
 
@@ -23,14 +23,17 @@
 		self.assertEquals(appointment.status, 'Open')
 		appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2))
 		self.assertEquals(appointment.status, 'Scheduled')
-		create_encounter(appointment)
+		encounter = create_encounter(appointment)
 		self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed')
+		encounter.cancel()
+		self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Open')
 
 	def test_start_encounter(self):
 		patient, medical_department, practitioner = create_healthcare_docs()
 		frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
 		appointment = create_appointment(patient, practitioner, add_days(nowdate(), 4), invoice = 1)
-		self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'), 1)
+		appointment.reload()
+		self.assertEqual(appointment.invoiced, 1)
 		encounter = make_encounter(appointment.name)
 		self.assertTrue(encounter)
 		self.assertEqual(encounter.company, appointment.company)
@@ -39,7 +42,7 @@
 		# invoiced flag mapped from appointment
 		self.assertEqual(encounter.invoiced, frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'))
 
-	def test_invoicing(self):
+	def test_auto_invoicing(self):
 		patient, medical_department, practitioner = create_healthcare_docs()
 		frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
 		frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 0)
@@ -55,6 +58,50 @@
 		self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'patient'), appointment.patient)
 		self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount)
 
+	def test_auto_invoicing_based_on_department(self):
+		patient, medical_department, practitioner = create_healthcare_docs()
+		frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
+		frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
+		appointment_type = create_appointment_type()
+
+		appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2),
+			invoice=1, appointment_type=appointment_type.name, department='_Test Medical Department')
+		appointment.reload()
+
+		self.assertEqual(appointment.invoiced, 1)
+		self.assertEqual(appointment.billing_item, 'HLC-SI-001')
+		self.assertEqual(appointment.paid_amount, 200)
+
+		sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent')
+		self.assertTrue(sales_invoice_name)
+		self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount)
+
+	def test_auto_invoicing_according_to_appointment_type_charge(self):
+		patient, medical_department, practitioner = create_healthcare_docs()
+		frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
+		frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
+
+		item = create_healthcare_service_items()
+		items = [{
+				'op_consulting_charge_item': item,
+				'op_consulting_charge': 300
+		}]
+		appointment_type = create_appointment_type(args={
+				'name': 'Generic Appointment Type charge',
+				'items': items
+			})
+
+		appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2),
+			invoice=1, appointment_type=appointment_type.name)
+		appointment.reload()
+
+		self.assertEqual(appointment.invoiced, 1)
+		self.assertEqual(appointment.billing_item, item)
+		self.assertEqual(appointment.paid_amount, 300)
+
+		sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent')
+		self.assertTrue(sales_invoice_name)
+
 	def test_appointment_cancel(self):
 		patient, medical_department, practitioner = create_healthcare_docs()
 		frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 1)
@@ -76,6 +123,59 @@
 		sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent')
 		self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'status'), 'Cancelled')
 
+	def test_appointment_booking_for_admission_service_unit(self):
+		from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge
+		from erpnext.healthcare.doctype.inpatient_record.test_inpatient_record import \
+			create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy
+
+		frappe.db.sql("""delete from `tabInpatient Record`""")
+		patient, medical_department, practitioner = create_healthcare_docs()
+		patient = create_patient()
+		# Schedule Admission
+		ip_record = create_inpatient(patient)
+		ip_record.expected_length_of_stay = 0
+		ip_record.save(ignore_permissions = True)
+
+		# Admit
+		service_unit = get_healthcare_service_unit('Test Service Unit Ip Occupancy')
+		admit_patient(ip_record, service_unit, now_datetime())
+
+		appointment = create_appointment(patient, practitioner, nowdate(), service_unit=service_unit)
+		self.assertEqual(appointment.service_unit, service_unit)
+
+		# Discharge
+		schedule_discharge(frappe.as_json({'patient': patient}))
+		ip_record1 = frappe.get_doc("Inpatient Record", ip_record.name)
+		mark_invoiced_inpatient_occupancy(ip_record1)
+		discharge_patient(ip_record1)
+
+	def test_invalid_healthcare_service_unit_validation(self):
+		from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge
+		from erpnext.healthcare.doctype.inpatient_record.test_inpatient_record import \
+			create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy
+
+		frappe.db.sql("""delete from `tabInpatient Record`""")
+		patient, medical_department, practitioner = create_healthcare_docs()
+		patient = create_patient()
+		# Schedule Admission
+		ip_record = create_inpatient(patient)
+		ip_record.expected_length_of_stay = 0
+		ip_record.save(ignore_permissions = True)
+
+		# Admit
+		service_unit = get_healthcare_service_unit('Test Service Unit Ip Occupancy')
+		admit_patient(ip_record, service_unit, now_datetime())
+
+		appointment_service_unit = get_healthcare_service_unit('Test Service Unit Ip Occupancy for Appointment')
+		appointment = create_appointment(patient, practitioner, nowdate(), service_unit=appointment_service_unit, save=0)
+		self.assertRaises(frappe.exceptions.ValidationError, appointment.save)
+
+		# Discharge
+		schedule_discharge(frappe.as_json({'patient': patient}))
+		ip_record1 = frappe.get_doc("Inpatient Record", ip_record.name)
+		mark_invoiced_inpatient_occupancy(ip_record1)
+		discharge_patient(ip_record1)
+
 
 def create_healthcare_docs():
 	patient = create_patient()
@@ -123,23 +223,28 @@
 		encounter.submit()
 		return encounter
 
-def create_appointment(patient, practitioner, appointment_date, invoice=0, procedure_template=0):
+def create_appointment(patient, practitioner, appointment_date, invoice=0, procedure_template=0,
+	service_unit=None, appointment_type=None, save=1, department=None):
 	item = create_healthcare_service_items()
 	frappe.db.set_value('Healthcare Settings', None, 'inpatient_visit_charge_item', item)
 	frappe.db.set_value('Healthcare Settings', None, 'op_consulting_charge_item', item)
 	appointment = frappe.new_doc('Patient Appointment')
 	appointment.patient = patient
 	appointment.practitioner = practitioner
-	appointment.department = '_Test Medical Department'
+	appointment.department = department or '_Test Medical Department'
 	appointment.appointment_date = appointment_date
 	appointment.company = '_Test Company'
 	appointment.duration = 15
+	if service_unit:
+		appointment.service_unit = service_unit
 	if invoice:
 		appointment.mode_of_payment = 'Cash'
-		appointment.paid_amount = 500
+	if appointment_type:
+		appointment.appointment_type = appointment_type
 	if procedure_template:
 		appointment.procedure_template = create_clinical_procedure_template().get('name')
-	appointment.save(ignore_permissions=True)
+	if save:
+		appointment.save(ignore_permissions=True)
 	return appointment
 
 def create_healthcare_service_items():
@@ -150,6 +255,7 @@
 	item.item_name = 'Consulting Charges'
 	item.item_group = 'Services'
 	item.is_stock_item = 0
+	item.stock_uom = 'Nos'
 	item.save()
 	return item.name
 
@@ -164,4 +270,29 @@
 	template.description = 'Knee Surgery and Rehab'
 	template.rate = 50000
 	template.save()
-	return template
\ No newline at end of file
+	return template
+
+def create_appointment_type(args=None):
+	if not args:
+		args =  frappe.local.form_dict
+
+	name = args.get('name') or 'Test Appointment Type wise Charge'
+
+	if frappe.db.exists('Appointment Type', name):
+		return frappe.get_doc('Appointment Type', name)
+
+	else:
+		item = create_healthcare_service_items()
+		items = [{
+				'medical_department': '_Test Medical Department',
+				'op_consulting_charge_item': item,
+				'op_consulting_charge': 200
+		}]
+		return frappe.get_doc({
+			'doctype': 'Appointment Type',
+			'appointment_type': args.get('name') or 'Test Appointment Type wise Charge',
+			'default_duration': args.get('default_duration') or 20,
+			'color': args.get('color') or '#7575ff',
+			'price_list': args.get('price_list') or frappe.db.get_value("Price List", {"selling": 1}),
+			'items': args.get('items') or items
+		}).insert()
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json
index 15675f4..b646ff9 100644
--- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json
+++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json
@@ -210,7 +210,7 @@
   {
    "fieldname": "drug_prescription",
    "fieldtype": "Table",
-   "label": "Items",
+   "label": "Drug Prescription",
    "options": "Drug Prescription"
   },
   {
@@ -328,7 +328,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2020-05-16 21:00:08.644531",
+ "modified": "2020-11-30 10:39:00.783119",
  "modified_by": "Administrator",
  "module": "Healthcare",
  "name": "Patient Encounter",
diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py
index 87f4249..cc21417 100644
--- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py
+++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py
@@ -17,10 +17,6 @@
 	def on_update(self):
 		if self.appointment:
 			frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Closed')
-		update_encounter_medical_record(self)
-
-	def after_insert(self):
-		insert_encounter_to_medical_record(self)
 
 	def on_submit(self):
 		if self.therapies:
@@ -33,8 +29,6 @@
 		if self.inpatient_record and self.drug_prescription:
 			delete_ip_medication_order(self)
 
-		delete_medical_record(self)
-
 	def set_title(self):
 		self.title = _('{0} with {1}').format(self.patient_name or self.patient,
 			self.practitioner_name or self.practitioner)[:100]
@@ -102,61 +96,7 @@
 			frappe.msgprint(_('Therapy Plan {0} created successfully.').format(frappe.bold(doc.name)), alert=True)
 
 
-def insert_encounter_to_medical_record(doc):
-	subject = set_subject_field(doc)
-	medical_record = frappe.new_doc('Patient Medical Record')
-	medical_record.patient = doc.patient
-	medical_record.subject = subject
-	medical_record.status = 'Open'
-	medical_record.communication_date = doc.encounter_date
-	medical_record.reference_doctype = 'Patient Encounter'
-	medical_record.reference_name = doc.name
-	medical_record.reference_owner = doc.owner
-	medical_record.save(ignore_permissions=True)
-
-
-def update_encounter_medical_record(encounter):
-	medical_record_id = frappe.db.exists('Patient Medical Record', {'reference_name': encounter.name})
-
-	if medical_record_id and medical_record_id[0][0]:
-		subject = set_subject_field(encounter)
-		frappe.db.set_value('Patient Medical Record', medical_record_id[0][0], 'subject', subject)
-	else:
-		insert_encounter_to_medical_record(encounter)
-
-
-def delete_medical_record(encounter):
-	record = frappe.db.exists('Patient Medical Record', {'reference_name', encounter.name})
-	if record:
-		frappe.delete_doc('Patient Medical Record', record, force=1)
-
 def delete_ip_medication_order(encounter):
 	record = frappe.db.exists('Inpatient Medication Order', {'patient_encounter': encounter.name})
 	if record:
-		frappe.delete_doc('Inpatient Medication Order', record, force=1)
-
-
-def set_subject_field(encounter):
-	subject = frappe.bold(_('Healthcare Practitioner: ')) + encounter.practitioner + '<br>'
-	if encounter.symptoms:
-		subject += frappe.bold(_('Symptoms: ')) + '<br>'
-		for entry in encounter.symptoms:
-			subject += cstr(entry.complaint) + '<br>'
-	else:
-		subject += frappe.bold(_('No Symptoms')) + '<br>'
-
-	if encounter.diagnosis:
-		subject += frappe.bold(_('Diagnosis: ')) + '<br>'
-		for entry in encounter.diagnosis:
-			subject += cstr(entry.diagnosis) + '<br>'
-	else:
-		subject += frappe.bold(_('No Diagnosis')) + '<br>'
-
-	if encounter.drug_prescription:
-		subject += '<br>' + _('Drug(s) Prescribed.')
-	if encounter.lab_test_prescription:
-		subject += '<br>' + _('Test(s) Prescribed.')
-	if encounter.procedure_prescription:
-		subject += '<br>' + _('Procedure(s) Prescribed.')
-
-	return subject
+		frappe.delete_doc('Inpatient Medication Order', record, force=1)
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/healthcare/doctype/patient_history_custom_document_type/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/healthcare/doctype/patient_history_custom_document_type/__init__.py
diff --git a/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json b/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json
new file mode 100644
index 0000000..3025c7b
--- /dev/null
+++ b/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.json
@@ -0,0 +1,55 @@
+{
+ "actions": [],
+ "creation": "2020-11-25 13:40:23.054469",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "document_type",
+  "date_fieldname",
+  "add_edit_fields",
+  "selected_fields"
+ ],
+ "fields": [
+  {
+   "fieldname": "document_type",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Document Type",
+   "options": "DocType",
+   "reqd": 1
+  },
+  {
+   "fieldname": "selected_fields",
+   "fieldtype": "Code",
+   "label": "Selected Fields",
+   "read_only": 1
+  },
+  {
+   "fieldname": "add_edit_fields",
+   "fieldtype": "Button",
+   "in_list_view": 1,
+   "label": "Add / Edit Fields"
+  },
+  {
+   "fieldname": "date_fieldname",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Date Fieldname",
+   "reqd": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2020-11-30 13:54:37.474671",
+ "modified_by": "Administrator",
+ "module": "Healthcare",
+ "name": "Patient History Custom Document Type",
+ "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/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.py b/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.py
new file mode 100644
index 0000000..f0a1f92
--- /dev/null
+++ b/erpnext/healthcare/doctype/patient_history_custom_document_type/patient_history_custom_document_type.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 PatientHistoryCustomDocumentType(Document):
+	pass
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/healthcare/doctype/patient_history_settings/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/healthcare/doctype/patient_history_settings/__init__.py
diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js
new file mode 100644
index 0000000..453da6a
--- /dev/null
+++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.js
@@ -0,0 +1,133 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Patient History Settings', {
+	refresh: function(frm) {
+		frm.set_query('document_type', 'custom_doctypes', () => {
+			return {
+				filters: {
+					custom: 1,
+					is_submittable: 1,
+					module: 'Healthcare',
+				}
+			};
+		});
+	},
+
+	field_selector: function(frm, doc, standard=1) {
+		let document_fields = [];
+		if (doc.selected_fields)
+			document_fields = (JSON.parse(doc.selected_fields)).map(f => f.fieldname);
+
+		frm.call({
+			method: 'get_doctype_fields',
+			doc: frm.doc,
+			args: {
+				document_type: doc.document_type,
+				fields: document_fields
+			},
+			freeze: true,
+			callback: function(r) {
+				if (r.message) {
+					let doctype = 'Patient History Custom Document Type';
+					if (standard)
+						doctype = 'Patient History Standard Document Type';
+
+					frm.events.show_field_selector_dialog(frm, doc, doctype, r.message);
+				}
+			}
+		});
+	},
+
+	show_field_selector_dialog: function(frm, doc, doctype, doc_fields) {
+		let d = new frappe.ui.Dialog({
+			title: __('{0} Fields', [__(doc.document_type)]),
+			fields: [
+				{
+					label: __('Select Fields'),
+					fieldtype: 'MultiCheck',
+					fieldname: 'fields',
+					options: doc_fields,
+					columns: 2
+				}
+			]
+		});
+
+		d.$body.prepend(`
+			<div class="columns-search">
+				<input type="text" placeholder="${__('Search')}" data-element="search" class="form-control input-xs">
+			</div>`
+		);
+
+		frappe.utils.setup_search(d.$body, '.unit-checkbox', '.label-area');
+
+		d.set_primary_action(__('Save'), () => {
+			let values = d.get_values().fields;
+
+			let selected_fields = [];
+
+			frappe.model.with_doctype(doc.document_type, function() {
+				for (let idx in values) {
+					let value = values[idx];
+
+					let field = frappe.get_meta(doc.document_type).fields.filter((df) => df.fieldname == value)[0];
+					if (field) {
+						selected_fields.push({
+							label: field.label,
+							fieldname: field.fieldname,
+							fieldtype: field.fieldtype
+						});
+					}
+				}
+
+				d.refresh();
+				frappe.model.set_value(doctype, doc.name, 'selected_fields', JSON.stringify(selected_fields));
+			});
+
+			d.hide();
+		});
+
+		d.show();
+	},
+
+	get_date_field_for_dt: function(frm, row) {
+		frm.call({
+			method: 'get_date_field_for_dt',
+			doc: frm.doc,
+			args: {
+				document_type: row.document_type
+			},
+			callback: function(data) {
+				if (data.message) {
+					frappe.model.set_value('Patient History Custom Document Type',
+						row.name, 'date_fieldname', data.message);
+				}
+			}
+		});
+	}
+});
+
+frappe.ui.form.on('Patient History Custom Document Type', {
+	document_type: function(frm, cdt, cdn) {
+		let row = locals[cdt][cdn];
+		if (row.document_type) {
+			frm.events.get_date_field_for_dt(frm, row);
+		}
+	},
+
+	add_edit_fields: function(frm, cdt, cdn) {
+		let row = locals[cdt][cdn];
+		if (row.document_type) {
+			frm.events.field_selector(frm, row, 0);
+		}
+	}
+});
+
+frappe.ui.form.on('Patient History Standard Document Type', {
+	add_edit_fields: function(frm, cdt, cdn) {
+		let row = locals[cdt][cdn];
+		if (row.document_type) {
+			frm.events.field_selector(frm, row);
+		}
+	}
+});
diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.json b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.json
new file mode 100644
index 0000000..143e2c9
--- /dev/null
+++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.json
@@ -0,0 +1,55 @@
+{
+ "actions": [],
+ "creation": "2020-11-25 13:41:37.675518",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "standard_doctypes",
+  "section_break_2",
+  "custom_doctypes"
+ ],
+ "fields": [
+  {
+   "fieldname": "section_break_2",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "custom_doctypes",
+   "fieldtype": "Table",
+   "label": "Custom Document Types",
+   "options": "Patient History Custom Document Type"
+  },
+  {
+   "fieldname": "standard_doctypes",
+   "fieldtype": "Table",
+   "label": "Standard Document Types",
+   "options": "Patient History Standard Document Type",
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "issingle": 1,
+ "links": [],
+ "modified": "2020-11-25 13:43:38.511771",
+ "modified_by": "Administrator",
+ "module": "Healthcare",
+ "name": "Patient History Settings",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "role": "System Manager",
+   "share": 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/healthcare/doctype/patient_history_settings/patient_history_settings.py b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py
new file mode 100644
index 0000000..2e8c994
--- /dev/null
+++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py
@@ -0,0 +1,188 @@
+# -*- 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
+import json
+from frappe import _
+from frappe.utils import cstr, cint
+from frappe.model.document import Document
+from erpnext.healthcare.page.patient_history.patient_history import get_patient_history_doctypes
+
+class PatientHistorySettings(Document):
+	def validate(self):
+		self.validate_submittable_doctypes()
+		self.validate_date_fieldnames()
+
+	def validate_submittable_doctypes(self):
+		for entry in self.custom_doctypes:
+			if not cint(frappe.db.get_value('DocType', entry.document_type, 'is_submittable')):
+				msg = _('Row #{0}: Document Type {1} is not submittable. ').format(
+					entry.idx, frappe.bold(entry.document_type))
+				msg += _('Patient Medical Record can only be created for submittable document types.')
+				frappe.throw(msg)
+
+	def validate_date_fieldnames(self):
+		for entry in self.custom_doctypes:
+			field = frappe.get_meta(entry.document_type).get_field(entry.date_fieldname)
+			if not field:
+				frappe.throw(_('Row #{0}: No such Field named {1} found in the Document Type {2}.').format(
+					entry.idx, frappe.bold(entry.date_fieldname), frappe.bold(entry.document_type)))
+
+			if field.fieldtype not in ['Date', 'Datetime']:
+				frappe.throw(_('Row #{0}: Field {1} in Document Type {2} is not a Date / Datetime field.').format(
+					entry.idx, frappe.bold(entry.date_fieldname), frappe.bold(entry.document_type)))
+
+	def get_doctype_fields(self, document_type, fields):
+		multicheck_fields = []
+		doc_fields = frappe.get_meta(document_type).fields
+
+		for field in doc_fields:
+			if field.fieldtype not in frappe.model.no_value_fields or \
+				field.fieldtype in frappe.model.table_fields and not field.hidden:
+				multicheck_fields.append({
+					'label': field.label,
+					'value': field.fieldname,
+					'checked': 1 if field.fieldname in fields else 0
+				})
+
+		return multicheck_fields
+
+	def get_date_field_for_dt(self, document_type):
+		meta = frappe.get_meta(document_type)
+		date_fields = meta.get('fields', {
+			'fieldtype': ['in', ['Date', 'Datetime']]
+		})
+
+		if date_fields:
+			return date_fields[0].get('fieldname')
+
+def create_medical_record(doc, method=None):
+	medical_record_required = validate_medical_record_required(doc)
+	if not medical_record_required:
+		return
+
+	if frappe.db.exists('Patient Medical Record', { 'reference_name': doc.name }):
+		return
+
+	subject = set_subject_field(doc)
+	date_field = get_date_field(doc.doctype)
+	medical_record = frappe.new_doc('Patient Medical Record')
+	medical_record.patient = doc.patient
+	medical_record.subject = subject
+	medical_record.status = 'Open'
+	medical_record.communication_date = doc.get(date_field)
+	medical_record.reference_doctype = doc.doctype
+	medical_record.reference_name = doc.name
+	medical_record.reference_owner = doc.owner
+	medical_record.save(ignore_permissions=True)
+
+
+def update_medical_record(doc, method=None):
+	medical_record_required = validate_medical_record_required(doc)
+	if not medical_record_required:
+		return
+
+	medical_record_id = frappe.db.exists('Patient Medical Record', { 'reference_name': doc.name })
+
+	if medical_record_id:
+		subject = set_subject_field(doc)
+		frappe.db.set_value('Patient Medical Record', medical_record_id[0][0], 'subject', subject)
+	else:
+		create_medical_record(doc)
+
+
+def delete_medical_record(doc, method=None):
+	medical_record_required = validate_medical_record_required(doc)
+	if not medical_record_required:
+		return
+
+	record = frappe.db.exists('Patient Medical Record', { 'reference_name': doc.name })
+	if record:
+		frappe.delete_doc('Patient Medical Record', record, force=1)
+
+
+def set_subject_field(doc):
+	from frappe.utils.formatters import format_value
+
+	meta = frappe.get_meta(doc.doctype)
+	subject = ''
+	patient_history_fields = get_patient_history_fields(doc)
+
+	for entry in patient_history_fields:
+		fieldname = entry.get('fieldname')
+		if entry.get('fieldtype') == 'Table' and doc.get(fieldname):
+			formatted_value = get_formatted_value_for_table_field(doc.get(fieldname), meta.get_field(fieldname))
+			subject += frappe.bold(_(entry.get('label')) + ': ') + '<br>' + cstr(formatted_value) + '<br>'
+
+		else:
+			if doc.get(fieldname):
+				formatted_value = format_value(doc.get(fieldname), meta.get_field(fieldname), doc)
+				subject += frappe.bold(_(entry.get('label')) + ': ') + cstr(formatted_value) + '<br>'
+
+	return subject
+
+
+def get_date_field(doctype):
+	dt = get_patient_history_config_dt(doctype)
+
+	return frappe.db.get_value(dt, { 'document_type': doctype }, 'date_fieldname')
+
+
+def get_patient_history_fields(doc):
+	dt = get_patient_history_config_dt(doc.doctype)
+	patient_history_fields = frappe.db.get_value(dt, { 'document_type': doc.doctype }, 'selected_fields')
+
+	if patient_history_fields:
+		return json.loads(patient_history_fields)
+
+
+def get_formatted_value_for_table_field(items, df):
+	child_meta = frappe.get_meta(df.options)
+
+	table_head = ''
+	table_row = ''
+	html = ''
+	create_head = True
+	for item in items:
+		table_row += '<tr>'
+		for cdf in child_meta.fields:
+			if cdf.in_list_view:
+				if create_head:
+					table_head += '<td>' + cdf.label + '</td>'
+				if item.get(cdf.fieldname):
+					table_row += '<td>' + str(item.get(cdf.fieldname)) + '</td>'
+				else:
+					table_row += '<td></td>'
+		create_head = False
+		table_row += '</tr>'
+
+	html += "<table class='table table-condensed table-bordered'>" + table_head +  table_row + "</table>"
+
+	return html
+
+
+def get_patient_history_config_dt(doctype):
+	if frappe.db.get_value('DocType', doctype, 'custom'):
+		return 'Patient History Custom Document Type'
+	else:
+		return 'Patient History Standard Document Type'
+
+
+def validate_medical_record_required(doc):
+	if frappe.flags.in_patch or frappe.flags.in_install or frappe.flags.in_setup_wizard \
+		or get_module(doc) != 'Healthcare':
+		return False
+
+	if doc.doctype not in get_patient_history_doctypes():
+		return False
+
+	return True
+
+def get_module(doc):
+	module = doc.meta.module
+	if not module:
+		module = frappe.db.get_value('DocType', doc.doctype, 'module')
+
+	return module
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient_history_settings/test_patient_history_settings.py b/erpnext/healthcare/doctype/patient_history_settings/test_patient_history_settings.py
new file mode 100644
index 0000000..c93b788
--- /dev/null
+++ b/erpnext/healthcare/doctype/patient_history_settings/test_patient_history_settings.py
@@ -0,0 +1,104 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+import json
+from frappe.utils import getdate
+from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_patient
+
+class TestPatientHistorySettings(unittest.TestCase):
+	def setUp(self):
+		dt = create_custom_doctype()
+		settings = frappe.get_single("Patient History Settings")
+		settings.append("custom_doctypes", {
+			"document_type": dt.name,
+			"date_fieldname": "date",
+			"selected_fields": json.dumps([{
+				"label": "Date",
+				"fieldname": "date",
+				"fieldtype": "Date"
+			},
+			{
+				"label": "Rating",
+				"fieldname": "rating",
+				"fieldtype": "Rating"
+			},
+			{
+				"label": "Feedback",
+				"fieldname": "feedback",
+				"fieldtype": "Small Text"
+			}])
+		})
+		settings.save()
+
+	def test_custom_doctype_medical_record(self):
+		# tests for medical record creation of standard doctypes in test_patient_medical_record.py
+		patient = create_patient()
+		doc = create_doc(patient)
+
+		# check for medical record
+		medical_rec = frappe.db.exists("Patient Medical Record", {"status": "Open", "reference_name": doc.name})
+		self.assertTrue(medical_rec)
+
+		medical_rec = frappe.get_doc("Patient Medical Record", medical_rec)
+		expected_subject = "<b>Date: </b>{0}<br><b>Rating: </b>3<br><b>Feedback: </b>Test Patient History Settings<br>".format(
+			frappe.utils.format_date(getdate()))
+		self.assertEqual(medical_rec.subject, expected_subject)
+		self.assertEqual(medical_rec.patient, patient)
+		self.assertEqual(medical_rec.communication_date, getdate())
+
+
+def create_custom_doctype():
+	if not frappe.db.exists("DocType", "Test Patient Feedback"):
+		doc = frappe.get_doc({
+				"doctype": "DocType",
+				"module": "Healthcare",
+				"custom": 1,
+				"is_submittable": 1,
+				"fields": [{
+					"label": "Date",
+					"fieldname": "date",
+					"fieldtype": "Date"
+				},
+				{
+					"label": "Patient",
+					"fieldname": "patient",
+					"fieldtype": "Link",
+					"options": "Patient"
+				},
+				{
+					"label": "Rating",
+					"fieldname": "rating",
+					"fieldtype": "Rating"
+				},
+				{
+					"label": "Feedback",
+					"fieldname": "feedback",
+					"fieldtype": "Small Text"
+				}],
+				"permissions": [{
+					"role": "System Manager",
+					"read": 1
+				}],
+				"name": "Test Patient Feedback",
+			})
+		doc.insert()
+		return doc
+	else:
+		return frappe.get_doc("DocType", "Test Patient Feedback")
+
+
+def create_doc(patient):
+	doc = frappe.get_doc({
+		"doctype": "Test Patient Feedback",
+		"patient": patient,
+		"date": getdate(),
+		"rating": 3,
+		"feedback": "Test Patient History Settings"
+	}).insert()
+	doc.submit()
+
+	return doc
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/healthcare/doctype/patient_history_standard_document_type/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/healthcare/doctype/patient_history_standard_document_type/__init__.py
diff --git a/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json
new file mode 100644
index 0000000..b43099c
--- /dev/null
+++ b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.json
@@ -0,0 +1,57 @@
+{
+ "actions": [],
+ "creation": "2020-11-25 13:39:36.014814",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "document_type",
+  "date_fieldname",
+  "add_edit_fields",
+  "selected_fields"
+ ],
+ "fields": [
+  {
+   "fieldname": "document_type",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Document Type",
+   "options": "DocType",
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "fieldname": "selected_fields",
+   "fieldtype": "Code",
+   "label": "Selected Fields",
+   "read_only": 1
+  },
+  {
+   "fieldname": "add_edit_fields",
+   "fieldtype": "Button",
+   "in_list_view": 1,
+   "label": "Add / Edit Fields"
+  },
+  {
+   "fieldname": "date_fieldname",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Date Fieldname",
+   "read_only": 1,
+   "reqd": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2020-11-30 13:54:56.773325",
+ "modified_by": "Administrator",
+ "module": "Healthcare",
+ "name": "Patient History Standard Document Type",
+ "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/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.py b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.py
new file mode 100644
index 0000000..2d94911
--- /dev/null
+++ b/erpnext/healthcare/doctype/patient_history_standard_document_type/patient_history_standard_document_type.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 PatientHistoryStandardDocumentType(Document):
+	pass
diff --git a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py
index 419d956..c1d9872 100644
--- a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py
+++ b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py
@@ -18,6 +18,7 @@
 		patient, medical_department, practitioner = create_healthcare_docs()
 		appointment = create_appointment(patient, practitioner, nowdate(), invoice=1)
 		encounter = create_encounter(appointment)
+
 		# check for encounter
 		medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': encounter.name})
 		self.assertTrue(medical_rec)
diff --git a/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py b/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py
index a061c66..7fb159d 100644
--- a/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py
+++ b/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py
@@ -5,10 +5,10 @@
 
 import frappe
 import unittest
-from frappe.utils import getdate, flt
+from frappe.utils import getdate, flt, nowdate
 from erpnext.healthcare.doctype.therapy_type.test_therapy_type import create_therapy_type
 from erpnext.healthcare.doctype.therapy_plan.therapy_plan import make_therapy_session, make_sales_invoice
-from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_healthcare_docs, create_patient
+from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_healthcare_docs, create_patient, create_appointment
 
 class TestTherapyPlan(unittest.TestCase):
 	def test_creation_on_encounter_submission(self):
@@ -28,6 +28,15 @@
 		frappe.get_doc(session).submit()
 		self.assertEquals(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'Completed')
 
+		patient, medical_department, practitioner = create_healthcare_docs()
+		appointment = create_appointment(patient, practitioner, nowdate())		
+		session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company', appointment.name)
+		session = frappe.get_doc(session)
+		session.submit()
+		self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed')
+		session.cancel()
+		self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Open')
+
 	def test_therapy_plan_from_template(self):
 		patient = create_patient()
 		template = create_therapy_plan_template()
diff --git a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py b/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py
index bc0ff1a..ac01c60 100644
--- a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py
+++ b/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py
@@ -47,7 +47,7 @@
 
 
 @frappe.whitelist()
-def make_therapy_session(therapy_plan, patient, therapy_type, company):
+def make_therapy_session(therapy_plan, patient, therapy_type, company, appointment=None):
 	therapy_type = frappe.get_doc('Therapy Type', therapy_type)
 
 	therapy_session = frappe.new_doc('Therapy Session')
@@ -58,6 +58,7 @@
 	therapy_session.duration = therapy_type.default_duration
 	therapy_session.rate = therapy_type.rate
 	therapy_session.exercises = therapy_type.exercises
+	therapy_session.appointment = appointment
 
 	if frappe.flags.in_test:
 		therapy_session.start_date = today()
diff --git a/erpnext/healthcare/doctype/therapy_session/therapy_session.js b/erpnext/healthcare/doctype/therapy_session/therapy_session.js
index a2b01c9..fd20003 100644
--- a/erpnext/healthcare/doctype/therapy_session/therapy_session.js
+++ b/erpnext/healthcare/doctype/therapy_session/therapy_session.js
@@ -19,6 +19,15 @@
 				}
 			};
 		});
+
+		frm.set_query('appointment', function() {
+
+			return {
+				filters: {
+					'status': ['in', ['Open', 'Scheduled']]
+				}
+			};
+		});
 	},
 
 	refresh: function(frm) {
diff --git a/erpnext/healthcare/doctype/therapy_session/therapy_session.py b/erpnext/healthcare/doctype/therapy_session/therapy_session.py
index 85d0970..51f267f 100644
--- a/erpnext/healthcare/doctype/therapy_session/therapy_session.py
+++ b/erpnext/healthcare/doctype/therapy_session/therapy_session.py
@@ -41,9 +41,15 @@
 
 	def on_submit(self):
 		self.update_sessions_count_in_therapy_plan()
-		insert_session_medical_record(self)
+
+	def on_update(self):
+		if self.appointment:
+			frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Closed')
 
 	def on_cancel(self):
+		if self.appointment:
+			frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Open')
+
 		self.update_sessions_count_in_therapy_plan(on_cancel=True)
 
 	def update_sessions_count_in_therapy_plan(self, on_cancel=False):
@@ -135,23 +141,3 @@
 	item.reference_dt = 'Therapy Session'
 	item.reference_dn = therapy.name
 	return item
-
-
-def insert_session_medical_record(doc):
-	subject = frappe.bold(_('Therapy: ')) + cstr(doc.therapy_type) + '<br>'
-	if doc.therapy_plan:
-		subject += frappe.bold(_('Therapy Plan: ')) + cstr(doc.therapy_plan) + '<br>'
-	if doc.practitioner:
-		subject += frappe.bold(_('Healthcare Practitioner: ')) + doc.practitioner
-	subject += frappe.bold(_('Total Counts Targeted: ')) + cstr(doc.total_counts_targeted) + '<br>'
-	subject += frappe.bold(_('Total Counts Completed: ')) + cstr(doc.total_counts_completed) + '<br>'
-
-	medical_record = frappe.new_doc('Patient Medical Record')
-	medical_record.patient = doc.patient
-	medical_record.subject = subject
-	medical_record.status = 'Open'
-	medical_record.communication_date = doc.start_date
-	medical_record.reference_doctype = 'Therapy Session'
-	medical_record.reference_name = doc.name
-	medical_record.reference_owner = doc.owner
-	medical_record.save(ignore_permissions=True)
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.py b/erpnext/healthcare/doctype/vital_signs/vital_signs.py
index 69d81ff..35c823d 100644
--- a/erpnext/healthcare/doctype/vital_signs/vital_signs.py
+++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.py
@@ -12,47 +12,7 @@
 	def validate(self):
 		self.set_title()
 
-	def on_submit(self):
-		insert_vital_signs_to_medical_record(self)
-
-	def on_cancel(self):
-		delete_vital_signs_from_medical_record(self)
-
 	def set_title(self):
 		self.title = _('{0} on {1}').format(self.patient_name or self.patient,
 			frappe.utils.format_date(self.signs_date))[:100]
 
-def insert_vital_signs_to_medical_record(doc):
-	subject = set_subject_field(doc)
-	medical_record = frappe.new_doc('Patient Medical Record')
-	medical_record.patient = doc.patient
-	medical_record.subject = subject
-	medical_record.status = 'Open'
-	medical_record.communication_date = doc.signs_date
-	medical_record.reference_doctype = 'Vital Signs'
-	medical_record.reference_name = doc.name
-	medical_record.reference_owner = doc.owner
-	medical_record.flags.ignore_mandatory = True
-	medical_record.save(ignore_permissions=True)
-
-def delete_vital_signs_from_medical_record(doc):
-	medical_record = frappe.db.get_value('Patient Medical Record', {'reference_name': doc.name})
-	if medical_record:
-		frappe.delete_doc('Patient Medical Record', medical_record)
-
-def set_subject_field(doc):
-	subject = ''
-	if doc.temperature:
-		subject += frappe.bold(_('Temperature: ')) + cstr(doc.temperature) + '<br>'
-	if doc.pulse:
-		subject += frappe.bold(_('Pulse: ')) + cstr(doc.pulse) + '<br>'
-	if doc.respiratory_rate:
-		subject += frappe.bold(_('Respiratory Rate: ')) + cstr(doc.respiratory_rate) + '<br>'
-	if doc.bp:
-		subject += frappe.bold(_('BP: ')) + cstr(doc.bp) + '<br>'
-	if doc.bmi:
-		subject += frappe.bold(_('BMI: ')) + cstr(doc.bmi) + '<br>'
-	if doc.nutrition_note:
-		subject += frappe.bold(_('Note: ')) + cstr(doc.nutrition_note) + '<br>'
-
-	return subject
diff --git a/erpnext/healthcare/page/patient_history/patient_history.css b/erpnext/healthcare/page/patient_history/patient_history.css
index 865d6ab..1bb5891 100644
--- a/erpnext/healthcare/page/patient_history/patient_history.css
+++ b/erpnext/healthcare/page/patient_history/patient_history.css
@@ -109,6 +109,11 @@
 	padding-right: 0px;
 }
 
+.patient-history-filter {
+	margin-left: 35px;
+	width: 25%;
+}
+
 #page-medical_record .plot-wrapper {
 	padding: 20px 15px;
 	border-bottom: 1px solid #d1d8dd;
diff --git a/erpnext/healthcare/page/patient_history/patient_history.html b/erpnext/healthcare/page/patient_history/patient_history.html
index 7a9446d..be486c6 100644
--- a/erpnext/healthcare/page/patient_history/patient_history.html
+++ b/erpnext/healthcare/page/patient_history/patient_history.html
@@ -1,6 +1,5 @@
 <div class="col-sm-12">
 	<div class="col-sm-3">
-	<p class="text-center">{%= __("Select Patient") %}</p>
 	<p class="patient" style="margin: auto; max-width: 300px; margin-bottom: 20px;"></p>
 	<div class="patient_details" style="z-index=0"></div>
 	</div>
@@ -11,6 +10,13 @@
 			<div id="chart" class="col-sm-12 patient_vital_charts">
 			</div>
 		</div>
+		<div class="header-separator col-sm-12 d-flex border-bottom py-3" style="display:none"></div>
+		<div class="row">
+			<div class="col-sm-12 d-flex">
+				<div class="patient-history-filter doctype-filter"></div>
+				<div class="patient-history-filter date-filter"></div>
+			</div>
+		</div>
 		<div class="col-sm-12 patient_documents_list">
 		</div>
 		<div class="col-sm-12 text-center py-3">
diff --git a/erpnext/healthcare/page/patient_history/patient_history.js b/erpnext/healthcare/page/patient_history/patient_history.js
index fe5b7bc..54343aa 100644
--- a/erpnext/healthcare/page/patient_history/patient_history.js
+++ b/erpnext/healthcare/page/patient_history/patient_history.js
@@ -1,141 +1,225 @@
-frappe.provide("frappe.patient_history");
+frappe.provide('frappe.patient_history');
 frappe.pages['patient_history'].on_page_load = function(wrapper) {
-	var me = this;
-	var page = frappe.ui.make_app_page({
+	let me = this;
+	let page = frappe.ui.make_app_page({
 		parent: wrapper,
 		title: 'Patient History',
 		single_column: true
 	});
 
-	frappe.breadcrumbs.add("Healthcare");
+	frappe.breadcrumbs.add('Healthcare');
 	let pid = '';
-	page.main.html(frappe.render_template("patient_history", {}));
-	var patient = frappe.ui.form.make_control({
-		parent: page.main.find(".patient"),
+	page.main.html(frappe.render_template('patient_history', {}));
+	page.main.find('.header-separator').hide();
+
+	let patient = frappe.ui.form.make_control({
+		parent: page.main.find('.patient'),
 		df: {
-			fieldtype: "Link",
-			options: "Patient",
-			fieldname: "patient",
-			change: function(){
-				if(pid != patient.get_value() && patient.get_value()){
+			fieldtype: 'Link',
+			options: 'Patient',
+			fieldname: 'patient',
+			placeholder: __('Select Patient'),
+			only_select: true,
+			change: function() {
+				let patient_id = patient.get_value();
+				if (pid != patient_id && patient_id) {
 					me.start = 0;
-					me.page.main.find(".patient_documents_list").html("");
-					get_documents(patient.get_value(), me);
-					show_patient_info(patient.get_value(), me);
-					show_patient_vital_charts(patient.get_value(), me, "bp", "mmHg", "Blood Pressure");
+					me.page.main.find('.patient_documents_list').html('');
+					setup_filters(patient_id, me);
+					get_documents(patient_id, me);
+					show_patient_info(patient_id, me);
+					show_patient_vital_charts(patient_id, me, 'bp', 'mmHg', 'Blood Pressure');
 				}
-				pid = patient.get_value();
+				pid = patient_id;
 			}
 		},
-		only_input: true,
 	});
 	patient.refresh();
 
-	if (frappe.route_options){
+	if (frappe.route_options) {
 		patient.set_value(frappe.route_options.patient);
 	}
 
-	this.page.main.on("click", ".btn-show-chart", function() {
-		var	btn_show_id = $(this).attr("data-show-chart-id"), pts = $(this).attr("data-pts");
-		var title = $(this).attr("data-title");
+	this.page.main.on('click', '.btn-show-chart', function() {
+		let	btn_show_id = $(this).attr('data-show-chart-id'), pts = $(this).attr('data-pts');
+		let title = $(this).attr('data-title');
 		show_patient_vital_charts(patient.get_value(), me, btn_show_id, pts, title);
 	});
 
-	this.page.main.on("click", ".btn-more", function() {
-		var	doctype = $(this).attr("data-doctype"), docname = $(this).attr("data-docname");
-		if(me.page.main.find("."+docname).parent().find('.document-html').attr('data-fetched') == "1"){
-			me.page.main.find("."+docname).hide();
-			me.page.main.find("."+docname).parent().find('.document-html').show();
-		}else{
-			if(doctype && docname){
-				let exclude = ["patient", "patient_name", 'patient_sex', "encounter_date"];
+	this.page.main.on('click', '.btn-more', function() {
+		let	doctype = $(this).attr('data-doctype'), docname = $(this).attr('data-docname');
+		if (me.page.main.find('.'+docname).parent().find('.document-html').attr('data-fetched') == '1') {
+			me.page.main.find('.'+docname).hide();
+			me.page.main.find('.'+docname).parent().find('.document-html').show();
+		} else {
+			if (doctype && docname) {
+				let exclude = ['patient', 'patient_name', 'patient_sex', 'encounter_date'];
 				frappe.call({
-					method: "erpnext.healthcare.utils.render_doc_as_html",
+					method: 'erpnext.healthcare.utils.render_doc_as_html',
 					args:{
 						doctype: doctype,
 						docname: docname,
 						exclude_fields: exclude
 					},
+					freeze: true,
 					callback: function(r) {
-						if (r.message){
-							me.page.main.find("."+docname).hide();
-							me.page.main.find("."+docname).parent().find('.document-html').html(r.message.html+"\
-							<div align='center'><a class='btn octicon octicon-chevron-up btn-default btn-xs\
-							btn-less' data-doctype='"+doctype+"' data-docname='"+docname+"'></a></div>");
-							me.page.main.find("."+docname).parent().find('.document-html').show();
-							me.page.main.find("."+docname).parent().find('.document-html').attr('data-fetched', "1");
+						if (r.message) {
+							me.page.main.find('.' + docname).hide();
+
+							me.page.main.find('.' + docname).parent().find('.document-html').html(
+								`${r.message.html}
+									<div align='center'>
+										<a class='btn octicon octicon-chevron-up btn-default btn-xs btn-less'
+											data-doctype='${doctype}'
+											data-docname='${docname}'>
+										</a>
+									</div>
+								`);
+
+							me.page.main.find('.' + docname).parent().find('.document-html').show();
+							me.page.main.find('.' + docname).parent().find('.document-html').attr('data-fetched', '1');
 						}
-					},
-					freeze: true
+					}
 				});
 			}
 		}
 	});
 
-	this.page.main.on("click", ".btn-less", function() {
-		var docname = $(this).attr("data-docname");
-		me.page.main.find("."+docname).parent().find('.document-id').show();
-		me.page.main.find("."+docname).parent().find('.document-html').hide();
+	this.page.main.on('click', '.btn-less', function() {
+		let docname = $(this).attr('data-docname');
+		me.page.main.find('.' + docname).parent().find('.document-id').show();
+		me.page.main.find('.' + docname).parent().find('.document-html').hide();
 	});
 	me.start = 0;
-	me.page.main.on("click", ".btn-get-records", function(){
+	me.page.main.on('click', '.btn-get-records', function() {
 		get_documents(patient.get_value(), me);
 	});
 };
 
-var get_documents = function(patient, me){
+let setup_filters = function(patient, me) {
+	$('.doctype-filter').empty();
+	frappe.xcall(
+		'erpnext.healthcare.page.patient_history.patient_history.get_patient_history_doctypes'
+	).then(document_types => {
+		let doctype_filter = frappe.ui.form.make_control({
+			parent: $('.doctype-filter'),
+			df: {
+				fieldtype: 'MultiSelectList',
+				fieldname: 'document_type',
+				placeholder: __('Select Document Type'),
+				input_class: 'input-xs',
+				change: () => {
+					me.start = 0;
+					me.page.main.find('.patient_documents_list').html('');
+					get_documents(patient, me, doctype_filter.get_value(), date_range_field.get_value());
+				},
+				get_data: () => {
+					return document_types.map(document_type => {
+						return {
+							description: document_type,
+							value: document_type
+						};
+					});
+				},
+			}
+		});
+		doctype_filter.refresh();
+
+		$('.date-filter').empty();
+		let date_range_field = frappe.ui.form.make_control({
+			df: {
+				fieldtype: 'DateRange',
+				fieldname: 'date_range',
+				placeholder: __('Date Range'),
+				input_class: 'input-xs',
+				change: () => {
+					let selected_date_range = date_range_field.get_value();
+					if (selected_date_range && selected_date_range.length === 2) {
+						me.start = 0;
+						me.page.main.find('.patient_documents_list').html('');
+						get_documents(patient, me, doctype_filter.get_value(), selected_date_range);
+					}
+				}
+			},
+			parent: $('.date-filter')
+		});
+		date_range_field.refresh();
+	});
+};
+
+let get_documents = function(patient, me, document_types="", selected_date_range="") {
+	let filters = {
+		name: patient,
+		start: me.start,
+		page_length: 20
+	};
+	if (document_types)
+		filters['document_types'] = document_types;
+	if (selected_date_range)
+		filters['date_range'] = selected_date_range;
+
 	frappe.call({
-		"method": "erpnext.healthcare.page.patient_history.patient_history.get_feed",
-		args: {
-			name: patient,
-			start: me.start,
-			page_length: 20
-		},
-		callback: function (r) {
-			var data = r.message;
-			if(data.length){
+		'method': 'erpnext.healthcare.page.patient_history.patient_history.get_feed',
+		args: filters,
+		callback: function(r) {
+			let data = r.message;
+			if (data.length) {
 				add_to_records(me, data);
-			}else{
-				me.page.main.find(".patient_documents_list").append("<div class='text-muted' align='center'><br><br>No more records..<br><br></div>");
-				me.page.main.find(".btn-get-records").hide();
+			} else {
+				me.page.main.find('.patient_documents_list').append(`
+					<div class='text-muted' align='center'>
+						<br><br>${__('No more records..')}<br><br>
+					</div>`);
+				me.page.main.find('.btn-get-records').hide();
 			}
 		}
 	});
 };
 
-var add_to_records = function(me, data){
-	var details = "<ul class='nav nav-pills nav-stacked'>";
-	var i;
-	for(i=0; i<data.length; i++){
-		if(data[i].reference_doctype){
+let add_to_records = function(me, data) {
+	let details = "<ul class='nav nav-pills nav-stacked'>";
+	let i;
+	for (i=0; i<data.length; i++) {
+		if (data[i].reference_doctype) {
 			let label = '';
-			if(data[i].subject){
-				label += "<br/>"+data[i].subject;
+			if (data[i].subject) {
+				label += "<br/>" + data[i].subject;
 			}
 			data[i] = add_date_separator(data[i]);
-			if(frappe.user_info(data[i].owner).image){
+
+			if (frappe.user_info(data[i].owner).image) {
 				data[i].imgsrc = frappe.utils.get_file_link(frappe.user_info(data[i].owner).image);
-			}
-			else{
+			} else {
 				data[i].imgsrc = false;
 			}
-			var time_line_heading = data[i].practitioner ? `${data[i].practitioner} ` : ``;
-			time_line_heading += data[i].reference_doctype + " - "+ data[i].reference_name;
-			details += `<li data-toggle='pill' class='patient_doc_menu'
-			data-doctype='${data[i].reference_doctype}' data-docname='${data[i].reference_name}'>
-			<div class='col-sm-12 d-flex border-bottom py-3'>`;
-			if (data[i].imgsrc){
-				details += `<span class='mr-3'>
-					<img class='avtar' src='${data[i].imgsrc}' width='32' height='32'>
-					</img>
-			</span>`;
-			}else{
-				details += `<span class='mr-3 avatar avatar-small' style='width:32px; height:32px;'><div align='center' class='standard-image'
-					style='background-color: #fafbfc;'>${data[i].practitioner ? data[i].practitioner.charAt(0) : "U"}</div></span>`;
+
+			let time_line_heading = data[i].practitioner ? `${data[i].practitioner} ` : ``;
+			time_line_heading += data[i].reference_doctype + " - " +
+				`<a onclick="frappe.set_route('Form', '${data[i].reference_doctype}', '${data[i].reference_name}');">
+					${data[i].reference_name}
+				</a>`;
+
+			details += `
+				<li data-toggle='pill' class='patient_doc_menu'
+					data-doctype='${data[i].reference_doctype}' data-docname='${data[i].reference_name}'>
+					<div class='col-sm-12 d-flex border-bottom py-3'>`;
+
+			if (data[i].imgsrc) {
+				details += `
+					<span class='mr-3'>
+						<img class='avtar' src='${data[i].imgsrc}' width='32' height='32'></img>
+					</span>`;
+			} else {
+				details += `<span class='mr-3 avatar avatar-small' style='width:32px; height:32px;'>
+					<div align='center' class='standard-image' style='background-color: #fafbfc;'>
+						${data[i].practitioner ? data[i].practitioner.charAt(0) : 'U'}
+					</div>
+				</span>`;
 			}
+
 			details += `<div class='d-flex flex-column width-full'>
 					<div>
-						`+time_line_heading+` on
+						`+time_line_heading+`
 							<span>
 								${data[i].date_sep}
 							</span>
@@ -156,133 +240,150 @@
 			</li>`;
 		}
 	}
-	details += "</ul>";
-	me.page.main.find(".patient_documents_list").append(details);
+
+	details += '</ul>';
+	me.page.main.find('.patient_documents_list').append(details);
 	me.start += data.length;
-	if(data.length===20){
+
+	if (data.length === 20) {
 		me.page.main.find(".btn-get-records").show();
-	}else{
+	} else {
 		me.page.main.find(".btn-get-records").hide();
-		me.page.main.find(".patient_documents_list").append("<div class='text-muted' align='center'><br><br>No more records..<br><br></div>");
+		me.page.main.find(".patient_documents_list").append(`
+			<div class='text-muted' align='center'>
+				<br><br>${__('No more records..')}<br><br>
+			</div>`);
 	}
 };
 
-var add_date_separator = function(data) {
-	var date = frappe.datetime.str_to_obj(data.creation);
+let add_date_separator = function(data) {
+	let date = frappe.datetime.str_to_obj(data.communication_date);
+	let pdate = '';
+	let diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date));
 
-	var diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date));
-	if(diff < 1) {
-		var pdate = 'Today';
-	} else if(diff < 2) {
-		pdate = 'Yesterday';
+	if (diff < 1) {
+		pdate = __('Today');
+	} else if (diff < 2) {
+		pdate = __('Yesterday');
 	} else {
-		pdate = frappe.datetime.global_date_format(date);
+		pdate = __('on ') + frappe.datetime.global_date_format(date);
 	}
 	data.date_sep = pdate;
 	return data;
 };
 
-var show_patient_info = function(patient, me){
+let show_patient_info = function(patient, me) {
 	frappe.call({
-		"method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail",
+		'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail',
 		args: {
 			patient: patient
 		},
-		callback: function (r) {
-			var data = r.message;
-			var details = "";
-			if(data.image){
-				details += "<div><img class='thumbnail' width=75% src='"+data.image+"'></div>";
+		callback: function(r) {
+			let data = r.message;
+			let details = '';
+			if (data.image) {
+				details += `<div><img class='thumbnail' width=75% src='${data.image}'></div>`;
 			}
-			details += "<b>" + data.patient_name +"</b><br>" + data.sex;
-			if(data.email) details += "<br>" + data.email;
-			if(data.mobile) details += "<br>" + data.mobile;
-			if(data.occupation) details += "<br><br><b>Occupation :</b> " + data.occupation;
-			if(data.blood_group) details += "<br><b>Blood group : </b> " + data.blood_group;
-			if(data.allergies) details +=  "<br><br><b>Allergies : </b> "+  data.allergies.replace("\n", "<br>");
-			if(data.medication) details +=  "<br><b>Medication : </b> "+  data.medication.replace("\n", "<br>");
-			if(data.alcohol_current_use) details +=  "<br><br><b>Alcohol use : </b> "+  data.alcohol_current_use;
-			if(data.alcohol_past_use) details +=  "<br><b>Alcohol past use : </b> "+  data.alcohol_past_use;
-			if(data.tobacco_current_use) details +=  "<br><b>Tobacco use : </b> "+  data.tobacco_current_use;
-			if(data.tobacco_past_use) details +=  "<br><b>Tobacco past use : </b> "+  data.tobacco_past_use;
-			if(data.medical_history) details +=  "<br><br><b>Medical history : </b> "+  data.medical_history.replace("\n", "<br>");
-			if(data.surgical_history) details +=  "<br><b>Surgical history : </b> "+  data.surgical_history.replace("\n", "<br>");
-			if(data.surrounding_factors) details +=  "<br><br><b>Occupational hazards : </b> "+  data.surrounding_factors.replace("\n", "<br>");
-			if(data.other_risk_factors) details += "<br><b>Other risk factors : </b> " + data.other_risk_factors.replace("\n", "<br>");
-			if(data.patient_details) details += "<br><br><b>More info : </b> " + data.patient_details.replace("\n", "<br>");
 
-			if(details){
-				details = "<div style='padding-left:10px; font-size:13px;' align='center'>" + details + "</div>";
+			details += `<b> ${data.patient_name} </b><br> ${data.sex}`;
+			if (data.email) details += `<br> ${data.email}`;
+			if (data.mobile) details += `<br> ${data.mobile}`;
+			if (data.occupation) details += `<br><br><b> ${__('Occupation')} : </b> ${data.occupation}`;
+			if (data.blood_group) details += `<br><b> ${__('Blood Group')} : </b> ${data.blood_group}`;
+			if (data.allergies) details +=  `<br><br><b> ${__('Allerigies')} : </b> ${data.allergies.replace("\n", ", ")}`;
+			if (data.medication) details +=  `<br><b> ${__('Medication')} : </b> ${data.medication.replace("\n", ", ")}`;
+			if (data.alcohol_current_use) details +=  `<br><br><b> ${__('Alcohol use')} : </b> ${data.alcohol_current_use}`;
+			if (data.alcohol_past_use) details +=  `<br><b> ${__('Alcohol past use')} : </b> ${data.alcohol_past_use}`;
+			if (data.tobacco_current_use) details +=  `<br><b> ${__('Tobacco use')} : </b> ${data.tobacco_current_use}`;
+			if (data.tobacco_past_use) details +=  `<br><b> ${__('Tobacco past use')} : </b> ${data.tobacco_past_use}`;
+			if (data.medical_history) details +=  `<br><br><b> ${__('Medical history')} : </b> ${data.medical_history.replace("\n", ", ")}`;
+			if (data.surgical_history) details +=  `<br><b> ${__('Surgical history')} : </b> ${data.surgical_history.replace("\n", ", ")}`;
+			if (data.surrounding_factors) details +=  `<br><br><b> ${__('Occupational hazards')} : </b> ${data.surrounding_factors.replace("\n", ", ")}`;
+			if (data.other_risk_factors) details += `<br><b> ${__('Other risk factors')} : </b> ${data.other_risk_factors.replace("\n", ", ")}`;
+			if (data.patient_details) details += `<br><br><b> ${__('More info')} : </b> ${data.patient_details.replace("\n", ", ")}`;
+
+			if (details) {
+				details = `<div style='padding-left:10px; font-size:13px;' align='left'>` + details + `</div>`;
 			}
-			me.page.main.find(".patient_details").html(details);
+			me.page.main.find('.patient_details').html(details);
 		}
 	});
 };
 
-var show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) {
+let show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) {
 	frappe.call({
-		method: "erpnext.healthcare.utils.get_patient_vitals",
+		method: 'erpnext.healthcare.utils.get_patient_vitals',
 		args:{
 			patient: patient
 		},
 		callback: function(r) {
-			if (r.message){
-				var show_chart_btns_html = "<div style='padding-top:5px;'><a class='btn btn-default btn-xs btn-show-chart' \
-				data-show-chart-id='bp' data-pts='mmHg' data-title='Blood Pressure'>Blood Pressure</a>\
-				<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='pulse_rate' \
-				data-pts='per Minutes' data-title='Respiratory/Pulse Rate'>Respiratory/Pulse Rate</a>\
-				<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='temperature' \
-				data-pts='°C or °F' data-title='Temperature'>Temperature</a>\
-				<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='bmi' \
-				data-pts='' data-title='BMI'>BMI</a></div>";
-				me.page.main.find(".show_chart_btns").html(show_chart_btns_html);
-				var data = r.message;
+			if (r.message) {
+				let show_chart_btns_html = `
+					<div style='padding-top:10px;'>
+						<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='bp' data-pts='mmHg' data-title='Blood Pressure'>
+							${__('Blood Pressure')}
+						</a>
+						<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='pulse_rate' data-pts='per Minutes' data-title='Respiratory/Pulse Rate'>
+							${__('Respiratory/Pulse Rate')}
+						</a>
+						<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='temperature' data-pts='°C or °F' data-title='Temperature'>
+							${__('Temperature')}
+						</a>
+						<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='bmi' data-pts='' data-title='BMI'>
+							${__('BMI')}
+						</a>
+					</div>`;
+
+				me.page.main.find('.show_chart_btns').html(show_chart_btns_html);
+				let data = r.message;
 				let labels = [], datasets = [];
 				let bp_systolic = [], bp_diastolic = [], temperature = [];
 				let pulse = [], respiratory_rate = [], bmi = [], height = [], weight = [];
-				for(var i=0; i<data.length; i++){
-					labels.push(data[i].signs_date+"||"+data[i].signs_time);
-					if(btn_show_id=="bp"){
+
+				for (let i=0; i<data.length; i++) {
+					labels.push(data[i].signs_date+'||'+data[i].signs_time);
+
+					if (btn_show_id === 'bp') {
 						bp_systolic.push(data[i].bp_systolic);
 						bp_diastolic.push(data[i].bp_diastolic);
 					}
-					if(btn_show_id=="temperature"){
+					if (btn_show_id === 'temperature') {
 						temperature.push(data[i].temperature);
 					}
-					if(btn_show_id=="pulse_rate"){
+					if (btn_show_id === 'pulse_rate') {
 						pulse.push(data[i].pulse);
 						respiratory_rate.push(data[i].respiratory_rate);
 					}
-					if(btn_show_id=="bmi"){
+					if (btn_show_id === 'bmi') {
 						bmi.push(data[i].bmi);
 						height.push(data[i].height);
 						weight.push(data[i].weight);
 					}
 				}
-				if(btn_show_id=="temperature"){
-					datasets.push({name: "Temperature", values: temperature, chartType:'line'});
+				if (btn_show_id === 'temperature') {
+					datasets.push({name: 'Temperature', values: temperature, chartType: 'line'});
 				}
-				if(btn_show_id=="bmi"){
-					datasets.push({name: "BMI", values: bmi, chartType:'line'});
-					datasets.push({name: "Height", values: height, chartType:'line'});
-					datasets.push({name: "Weight", values: weight, chartType:'line'});
+				if (btn_show_id === 'bmi') {
+					datasets.push({name: 'BMI', values: bmi, chartType: 'line'});
+					datasets.push({name: 'Height', values: height, chartType: 'line'});
+					datasets.push({name: 'Weight', values: weight, chartType: 'line'});
 				}
-				if(btn_show_id=="bp"){
-					datasets.push({name: "BP Systolic", values: bp_systolic, chartType:'line'});
-					datasets.push({name: "BP Diastolic", values: bp_diastolic, chartType:'line'});
+				if (btn_show_id === 'bp') {
+					datasets.push({name: 'BP Systolic', values: bp_systolic, chartType: 'line'});
+					datasets.push({name: 'BP Diastolic', values: bp_diastolic, chartType: 'line'});
 				}
-				if(btn_show_id=="pulse_rate"){
-					datasets.push({name: "Heart Rate / Pulse", values: pulse, chartType:'line'});
-					datasets.push({name: "Respiratory Rate", values: respiratory_rate, chartType:'line'});
+				if (btn_show_id === 'pulse_rate') {
+					datasets.push({name: 'Heart Rate / Pulse', values: pulse, chartType: 'line'});
+					datasets.push({name: 'Respiratory Rate', values: respiratory_rate, chartType: 'line'});
 				}
-				new frappe.Chart( ".patient_vital_charts", {
+				new frappe.Chart('.patient_vital_charts', {
 					data: {
 						labels: labels,
 						datasets: datasets
 					},
 
 					title: title,
-					type: 'axis-mixed', // 'axis-mixed', 'bar', 'line', 'pie', 'percentage'
+					type: 'axis-mixed',
 					height: 200,
 					colors: ['purple', '#ffa3ef', 'light-blue'],
 
@@ -291,9 +392,11 @@
 						formatTooltipY: d => d + ' ' + pts,
 					}
 				});
-			}else{
-				me.page.main.find(".patient_vital_charts").html("");
-				me.page.main.find(".show_chart_btns").html("");
+				me.page.main.find('.header-separator').show();
+			} else {
+				me.page.main.find('.patient_vital_charts').html('');
+				me.page.main.find('.show_chart_btns').html('');
+				me.page.main.find('.header-separator').hide();
 			}
 		}
 	});
diff --git a/erpnext/healthcare/page/patient_history/patient_history.py b/erpnext/healthcare/page/patient_history/patient_history.py
index 772aa4e..4cdfd64 100644
--- a/erpnext/healthcare/page/patient_history/patient_history.py
+++ b/erpnext/healthcare/page/patient_history/patient_history.py
@@ -4,36 +4,70 @@
 
 from __future__ import unicode_literals
 import frappe
+import json
 from frappe.utils import cint
 from erpnext.healthcare.utils import render_docs_as_html
 
 @frappe.whitelist()
-def get_feed(name, start=0, page_length=20):
+def get_feed(name, document_types=None, date_range=None, start=0, page_length=20):
 	"""get feed"""
-	result = frappe.db.sql("""select name, owner, creation,
-		reference_doctype, reference_name, subject
-		from `tabPatient Medical Record`
-		where patient=%(patient)s
-		order by creation desc
-		limit %(start)s, %(page_length)s""",
-		{
-			"patient": name,
-			"start": cint(start),
-			"page_length": cint(page_length)
-		}, as_dict=True)
+	filters = get_filters(name, document_types, date_range)
+
+	result = frappe.db.get_all('Patient Medical Record',
+		fields=['name', 'owner', 'communication_date',
+			'reference_doctype', 'reference_name', 'subject'],
+		filters=filters,
+		order_by='communication_date DESC',
+		limit=cint(page_length),
+		start=cint(start)
+	)
+
 	return result
 
+
+def get_filters(name, document_types=None, date_range=None):
+	filters = {'patient': name}
+	if document_types:
+		document_types = json.loads(document_types)
+		if len(document_types):
+			filters['reference_doctype'] = ['IN', document_types]
+
+	if date_range:
+		try:
+			date_range = json.loads(date_range)
+			if date_range:
+				filters['communication_date'] = ['between', [date_range[0], date_range[1]]]
+		except json.decoder.JSONDecodeError:
+			pass
+
+	return filters
+
+
 @frappe.whitelist()
 def get_feed_for_dt(doctype, docname):
 	"""get feed"""
-	result = frappe.db.sql("""select name, owner, modified, creation,
-			reference_doctype, reference_name, subject
-		from `tabPatient Medical Record`
-		where reference_name=%(docname)s and reference_doctype=%(doctype)s
-		order by creation desc""",
-		{
-			"docname": docname,
-			"doctype": doctype
-		}, as_dict=True)
+	result = frappe.db.get_all('Patient Medical Record',
+		fields=['name', 'owner', 'communication_date',
+			'reference_doctype', 'reference_name', 'subject'],
+		filters={
+			'reference_doctype': doctype,
+			'reference_name': docname
+		},
+		order_by='communication_date DESC'
+	)
 
 	return result
+
+
+@frappe.whitelist()
+def get_patient_history_doctypes():
+	document_types = []
+	settings = frappe.get_single("Patient History Settings")
+
+	for entry in settings.standard_doctypes:
+		document_types.append(entry.document_type)
+
+	for entry in settings.custom_doctypes:
+		document_types.append(entry.document_type)
+
+	return document_types
diff --git a/erpnext/healthcare/setup.py b/erpnext/healthcare/setup.py
index 0684080..bf4df7e 100644
--- a/erpnext/healthcare/setup.py
+++ b/erpnext/healthcare/setup.py
@@ -16,6 +16,7 @@
 	create_healthcare_item_groups()
 	create_sensitivity()
 	add_healthcare_service_unit_tree_root()
+	setup_patient_history_settings()
 
 def create_medical_departments():
 	departments = [
@@ -213,3 +214,82 @@
 		if company:
 			return company[0].name
 	return None
+
+def setup_patient_history_settings():
+	import json
+
+	settings = frappe.get_single('Patient History Settings')
+	configuration = get_patient_history_config()
+	for dt, config in configuration.items():
+		settings.append("standard_doctypes", {
+			"document_type": dt,
+			"date_fieldname": config[0],
+			"selected_fields": json.dumps(config[1])
+		})
+	settings.save()
+
+def get_patient_history_config():
+	return {
+		"Patient Encounter": ("encounter_date", [
+			{"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"},
+			{"label": "Symptoms", "fieldname": "symptoms", "fieldtype": "Table Multiselect"},
+			{"label": "Diagnosis", "fieldname": "diagnosis", "fieldtype": "Table Multiselect"},
+			{"label": "Drug Prescription", "fieldname": "drug_prescription", "fieldtype": "Table"},
+			{"label": "Lab Tests", "fieldname": "lab_test_prescription", "fieldtype": "Table"},
+			{"label": "Clinical Procedures", "fieldname": "procedure_prescription", "fieldtype": "Table"},
+			{"label": "Therapies", "fieldname": "therapies", "fieldtype": "Table"},
+			{"label": "Review Details", "fieldname": "encounter_comment", "fieldtype": "Small Text"}
+		]),
+		"Clinical Procedure": ("start_date", [
+			{"label": "Procedure Template", "fieldname": "procedure_template", "fieldtype": "Link"},
+			{"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"},
+			{"label": "Notes", "fieldname": "notes", "fieldtype": "Small Text"},
+			{"label": "Service Unit", "fieldname": "service_unit", "fieldtype": "Healthcare Service Unit"},
+			{"label": "Start Time", "fieldname": "start_time", "fieldtype": "Time"},
+			{"label": "Sample", "fieldname": "sample", "fieldtype": "Link"}
+		]),
+		"Lab Test": ("result_date", [
+			{"label": "Test Template", "fieldname": "template", "fieldtype": "Link"},
+			{"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"},
+			{"label": "Test Name", "fieldname": "lab_test_name", "fieldtype": "Data"},
+			{"label": "Lab Technician Name", "fieldname": "employee_name", "fieldtype": "Data"},
+			{"label": "Sample ID", "fieldname": "sample", "fieldtype": "Link"},
+			{"label": "Normal Test Result", "fieldname": "normal_test_items", "fieldtype": "Table"},
+			{"label": "Descriptive Test Result", "fieldname": "descriptive_test_items", "fieldtype": "Table"},
+			{"label": "Organism Test Result", "fieldname": "organism_test_items", "fieldtype": "Table"},
+			{"label": "Sensitivity Test Result", "fieldname": "sensitivity_test_items", "fieldtype": "Table"},
+			{"label": "Comments", "fieldname": "lab_test_comment", "fieldtype": "Table"}
+		]),
+		"Therapy Session": ("start_date", [
+			{"label": "Therapy Type", "fieldname": "therapy_type", "fieldtype": "Link"},
+			{"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"},
+			{"label": "Therapy Plan", "fieldname": "therapy_plan", "fieldtype": "Link"},
+			{"label": "Duration", "fieldname": "duration", "fieldtype": "Int"},
+			{"label": "Location", "fieldname": "location", "fieldtype": "Link"},
+			{"label": "Healthcare Service Unit", "fieldname": "service_unit", "fieldtype": "Link"},
+			{"label": "Start Time", "fieldname": "start_time", "fieldtype": "Time"},
+			{"label": "Exercises", "fieldname": "exercises", "fieldtype": "Table"},
+			{"label": "Total Counts Targeted", "fieldname": "total_counts_targeted", "fieldtype": "Int"},
+			{"label": "Total Counts Completed", "fieldname": "total_counts_completed", "fieldtype": "Int"}
+		]),
+		"Vital Signs": ("signs_date", [
+			{"label": "Body Temperature", "fieldname": "temperature", "fieldtype": "Data"},
+			{"label": "Heart Rate / Pulse", "fieldname": "pulse", "fieldtype": "Data"},
+			{"label": "Respiratory rate", "fieldname": "respiratory_rate", "fieldtype": "Data"},
+			{"label": "Tongue", "fieldname": "tongue", "fieldtype": "Select"},
+			{"label": "Abdomen", "fieldname": "abdomen", "fieldtype": "Select"},
+			{"label": "Reflexes", "fieldname": "reflexes", "fieldtype": "Select"},
+			{"label": "Blood Pressure", "fieldname": "bp", "fieldtype": "Data"},
+			{"label": "Notes", "fieldname": "vital_signs_note", "fieldtype": "Small Text"},
+			{"label": "Height (In Meter)", "fieldname": "height", "fieldtype": "Float"},
+			{"label": "Weight (In Kilogram)", "fieldname": "weight", "fieldtype": "Float"},
+			{"label": "BMI", "fieldname": "bmi", "fieldtype": "Float"}
+		]),
+		"Inpatient Medication Order": ("start_date", [
+			{"label": "Healthcare Practitioner", "fieldname": "practitioner", "fieldtype": "Link"},
+			{"label": "Start Date", "fieldname": "start_date", "fieldtype": "Date"},
+			{"label": "End Date", "fieldname": "end_date", "fieldtype": "Date"},
+			{"label": "Medication Orders", "fieldname": "medication_orders", "fieldtype": "Table"},
+			{"label": "Total Orders", "fieldname": "total_orders", "fieldtype": "Float"}
+		])
+	}
\ No newline at end of file
diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py
index 96282f5..510ac9e 100644
--- a/erpnext/healthcare/utils.py
+++ b/erpnext/healthcare/utils.py
@@ -5,8 +5,11 @@
 from __future__ import unicode_literals
 import math
 import frappe
+import json
 from frappe import _
+from frappe.utils.formatters import format_value
 from frappe.utils import time_diff_in_hours, rounded
+from six import string_types
 from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account
 from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity
 from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple
@@ -63,7 +66,9 @@
 			income_account = None
 			service_item = None
 			if appointment.practitioner:
-				service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment)
+				details = get_service_item_and_practitioner_charge(appointment)
+				service_item = details.get('service_item')
+				practitioner_charge = details.get('practitioner_charge')
 				income_account = get_income_account(appointment.practitioner, appointment.company)
 			appointments_to_invoice.append({
 				'reference_type': 'Patient Appointment',
@@ -77,11 +82,13 @@
 
 
 def get_encounters_to_invoice(patient, company):
+	if not isinstance(patient, str):
+		patient = patient.name
 	encounters_to_invoice = []
 	encounters = frappe.get_list(
 		'Patient Encounter',
 		fields=['*'],
-		filters={'patient': patient.name, 'company': company, 'invoiced': False, 'docstatus': 1}
+		filters={'patient': patient, 'company': company, 'invoiced': False, 'docstatus': 1}
 	)
 	if encounters:
 		for encounter in encounters:
@@ -90,7 +97,13 @@
 				income_account = None
 				service_item = None
 				if encounter.practitioner:
-					service_item, practitioner_charge = get_service_item_and_practitioner_charge(encounter)
+					if encounter.inpatient_record and \
+						frappe.db.get_single_value('Healthcare Settings', 'do_not_bill_inpatient_encounters'):
+						continue
+
+					details = get_service_item_and_practitioner_charge(encounter)
+					service_item = details.get('service_item')
+					practitioner_charge = details.get('practitioner_charge')
 					income_account = get_income_account(encounter.practitioner, encounter.company)
 
 				encounters_to_invoice.append({
@@ -166,7 +179,7 @@
 		if procedure.invoice_separately_as_consumables and procedure.consume_stock \
 			and procedure.status == 'Completed' and not procedure.consumption_invoiced:
 
-			service_item = get_healthcare_service_item('clinical_procedure_consumable_item')
+			service_item = frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item')
 			if not service_item:
 				msg = _('Please Configure Clinical Procedure Consumable Item in ')
 				msg += '''<b><a href='#Form/Healthcare Settings'>Healthcare Settings</a></b>'''
@@ -297,24 +310,50 @@
 
 	return therapy_sessions_to_invoice
 
-
+@frappe.whitelist()
 def get_service_item_and_practitioner_charge(doc):
+	if isinstance(doc, string_types):
+		doc = json.loads(doc)
+		doc = frappe.get_doc(doc)
+
+	service_item = None
+	practitioner_charge = None
+	department = doc.medical_department if doc.doctype == 'Patient Encounter' else doc.department
+
 	is_inpatient = doc.inpatient_record
-	if is_inpatient:
-		service_item = get_practitioner_service_item(doc.practitioner, 'inpatient_visit_charge_item')
+
+	if doc.get('appointment_type'):
+		service_item, practitioner_charge = get_appointment_type_service_item(doc.appointment_type, department, is_inpatient)
+
+	if not service_item and not practitioner_charge:
+		service_item, practitioner_charge = get_practitioner_service_item(doc.practitioner, is_inpatient)
 		if not service_item:
-			service_item = get_healthcare_service_item('inpatient_visit_charge_item')
-	else:
-		service_item = get_practitioner_service_item(doc.practitioner, 'op_consulting_charge_item')
-		if not service_item:
-			service_item = get_healthcare_service_item('op_consulting_charge_item')
+			service_item = get_healthcare_service_item(is_inpatient)
+
 	if not service_item:
 		throw_config_service_item(is_inpatient)
 
-	practitioner_charge = get_practitioner_charge(doc.practitioner, is_inpatient)
 	if not practitioner_charge:
 		throw_config_practitioner_charge(is_inpatient, doc.practitioner)
 
+	return {'service_item': service_item, 'practitioner_charge': practitioner_charge}
+
+
+def get_appointment_type_service_item(appointment_type, department, is_inpatient):
+	from erpnext.healthcare.doctype.appointment_type.appointment_type import get_service_item_based_on_department
+
+	item_list = get_service_item_based_on_department(appointment_type, department)
+	service_item = None
+	practitioner_charge = None
+
+	if item_list:
+		if is_inpatient:
+			service_item = item_list.get('inpatient_visit_charge_item')
+			practitioner_charge = item_list.get('inpatient_visit_charge')
+		else:
+			service_item = item_list.get('op_consulting_charge_item')
+			practitioner_charge = item_list.get('op_consulting_charge')
+
 	return service_item, practitioner_charge
 
 
@@ -338,12 +377,27 @@
 	frappe.throw(msg, title=_('Missing Configuration'))
 
 
-def get_practitioner_service_item(practitioner, service_item_field):
-	return frappe.db.get_value('Healthcare Practitioner', practitioner, service_item_field)
+def get_practitioner_service_item(practitioner, is_inpatient):
+	service_item = None
+	practitioner_charge = None
+
+	if is_inpatient:
+		service_item, practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, ['inpatient_visit_charge_item', 'inpatient_visit_charge'])
+	else:
+		service_item, practitioner_charge = frappe.db.get_value('Healthcare Practitioner', practitioner, ['op_consulting_charge_item', 'op_consulting_charge'])
+
+	return service_item, practitioner_charge
 
 
-def get_healthcare_service_item(service_item_field):
-	return frappe.db.get_single_value('Healthcare Settings', service_item_field)
+def get_healthcare_service_item(is_inpatient):
+	service_item = None
+
+	if is_inpatient:
+		service_item = frappe.db.get_single_value('Healthcare Settings', 'inpatient_visit_charge_item')
+	else:
+		service_item = frappe.db.get_single_value('Healthcare Settings', 'op_consulting_charge_item')
+
+	return service_item
 
 
 def get_practitioner_charge(practitioner, is_inpatient):
@@ -374,7 +428,8 @@
 		invoiced = True
 
 	if item.reference_dt == 'Clinical Procedure':
-		if get_healthcare_service_item('clinical_procedure_consumable_item') == item.item_code:
+		service_item = frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item')
+		if service_item == item.item_code:
 			frappe.db.set_value(item.reference_dt, item.reference_dn, 'consumption_invoiced', invoiced)
 		else:
 			frappe.db.set_value(item.reference_dt, item.reference_dn, 'invoiced', invoiced)
@@ -396,7 +451,8 @@
 
 
 def validate_invoiced_on_submit(item):
-	if item.reference_dt == 'Clinical Procedure' and get_healthcare_service_item('clinical_procedure_consumable_item') == item.item_code:
+	if item.reference_dt == 'Clinical Procedure' and \
+		frappe.db.get_single_value('Healthcare Settings', 'clinical_procedure_consumable_item') == item.item_code:
 		is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, 'consumption_invoiced')
 	else:
 		is_invoiced = frappe.db.get_value(item.reference_dt, item.reference_dn, 'invoiced')
@@ -642,11 +698,15 @@
 				html += "<table class='table table-condensed table-bordered'>" \
 				+ table_head +  table_row + "</table>"
 			continue
+
 		#on other field types add label and value to html
 		if not df.hidden and not df.print_hide and doc.get(df.fieldname) and df.fieldname not in exclude_fields:
-			html +=  '<br>{0} : {1}'.format(df.label or df.fieldname, \
-			doc.get(df.fieldname))
+			if doc.get(df.fieldname):
+				formatted_value = format_value(doc.get(df.fieldname), meta.get_field(df.fieldname), doc)
+				html +=  '<br>{0} : {1}'.format(df.label or df.fieldname, formatted_value)
+
 			if not has_data : has_data = True
+
 	if sec_on and col_on and has_data:
 		doc_html += section_html + html + '</div></div>'
 	elif sec_on and not col_on and has_data:
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index a2d9d86..fe80c65 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -221,6 +221,11 @@
 }
 
 doc_events = {
+	"*": {
+		"on_submit": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.create_medical_record",
+		"on_update_after_submit": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.update_medical_record",
+		"on_cancel": "erpnext.healthcare.doctype.patient_history_settings.patient_history_settings.delete_medical_record"
+	},
 	"Stock Entry": {
 		"on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty",
 		"on_cancel": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty"
@@ -341,7 +346,8 @@
 		"erpnext.selling.doctype.quotation.quotation.set_expired_status",
 		"erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_appointment_status",
 		"erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status",
-		"erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_auto_email"
+		"erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_auto_email",
+		"erpnext.non_profit.doctype.membership.membership.set_expired_status"
 	],
 	"daily_long": [
 		"erpnext.setup.doctype.email_digest.email_digest.send",
@@ -351,13 +357,13 @@
 		"erpnext.hr.utils.generate_leave_encashment",
 		"erpnext.hr.utils.allocate_earned_leaves",
 		"erpnext.hr.utils.grant_leaves_automatically",
-		"erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall.create_process_loan_security_shortfall",
-		"erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual_for_term_loans",
+		"erpnext.loan_management.doctype.process_loan_security_shortfall.process_loan_security_shortfall.create_process_loan_security_shortfall",
+		"erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual.process_loan_interest_accrual_for_term_loans",
 		"erpnext.crm.doctype.lead.lead.daily_open_lead"
 	],
 	"monthly_long": [
 		"erpnext.accounts.deferred_revenue.process_deferred_accounting",
-		"erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual_for_demand_loans"
+		"erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual.process_loan_interest_accrual_for_demand_loans"
 	]
 }
 
@@ -410,9 +416,6 @@
 	'Italy': {
 		'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.italy.utils.update_itemised_tax_data',
 		'erpnext.controllers.accounts_controller.validate_regional': 'erpnext.regional.italy.utils.sales_invoice_validate',
-	},
-	'Germany': {
-		'erpnext.controllers.accounts_controller.validate_regional': 'erpnext.regional.germany.accounts_controller.validate_regional',
 	}
 }
 user_privacy_documents = [
diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json
index 4f1c04f..dc2aaa4 100644
--- a/erpnext/hr/doctype/employee/employee.json
+++ b/erpnext/hr/doctype/employee/employee.json
@@ -813,7 +813,7 @@
  "idx": 24,
  "image_field": "image",
  "links": [],
- "modified": "2020-10-16 15:02:04.283657",
+ "modified": "2021-01-01 16:54:33.477439",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Employee",
@@ -855,7 +855,6 @@
    "write": 1
   }
  ],
- "quick_entry": 1,
  "search_fields": "employee_name",
  "show_name_in_global_search": 1,
  "sort_field": "modified",
diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py
index dfc600c..0fde3a1 100755
--- a/erpnext/hr/doctype/employee/employee.py
+++ b/erpnext/hr/doctype/employee/employee.py
@@ -135,7 +135,7 @@
 				try:
 					frappe.get_doc({
 						"doctype": "File",
-						"file_name": self.image,
+						"file_url": self.image,
 						"attached_to_doctype": "User",
 						"attached_to_name": self.user_id
 					}).insert()
diff --git a/erpnext/hr/doctype/employee/test_employee.py b/erpnext/hr/doctype/employee/test_employee.py
index f4b214a..b88daaa 100644
--- a/erpnext/hr/doctype/employee/test_employee.py
+++ b/erpnext/hr/doctype/employee/test_employee.py
@@ -16,6 +16,7 @@
 		employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0])
 		employee.date_of_birth = "1992" + frappe.utils.nowdate()[4:]
 		employee.company_email = "test@example.com"
+		employee.company = "_Test Company"
 		employee.save()
 
 		from erpnext.hr.doctype.employee.employee import get_employees_who_are_born_today, send_birthday_reminders
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js
index 7056adf..5037ceb 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.js
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.js
@@ -18,13 +18,18 @@
 			if (!frm.doc.employee) {
 				frappe.msgprint(__("Please select employee first"));
 			}
-			var company_currency = erpnext.get_currency(frm.doc.company);
+			let company_currency = erpnext.get_currency(frm.doc.company);
+			let currencies = [company_currency];
+			if (frm.doc.currency && (frm.doc.currency != company_currency)) {
+				currencies.push(frm.doc.currency);
+			}
+
 			return {
 				filters: {
 					"root_type": "Asset",
 					"is_group": 0,
 					"company": frm.doc.company,
-					"account_currency": ["in", [frm.doc.currency, company_currency]],
+					"account_currency": ["in", currencies],
 				}
 			};
 		});
@@ -181,21 +186,23 @@
 	},
 
 	currency: function(frm) {
-		var from_currency = frm.doc.currency;
-		var company_currency;
-		if (!frm.doc.company) {
-			company_currency = erpnext.get_currency(frappe.defaults.get_default("Company"));
-		} else {
-			company_currency = erpnext.get_currency(frm.doc.company);
+		if (frm.doc.currency) {
+			var from_currency = frm.doc.currency;
+			var company_currency;
+			if (!frm.doc.company) {
+				company_currency = erpnext.get_currency(frappe.defaults.get_default("Company"));
+			} else {
+				company_currency = erpnext.get_currency(frm.doc.company);
+			}
+			if (from_currency != company_currency) {
+				frm.events.set_exchange_rate(frm, from_currency, company_currency);
+			} else {
+				frm.set_value("exchange_rate", 1.0);
+				frm.set_df_property('exchange_rate', 'hidden', 1);
+				frm.set_df_property("exchange_rate", "description", "" );
+			}
+			frm.refresh_fields();
 		}
-		if (from_currency != company_currency) {
-			frm.events.set_exchange_rate(frm, from_currency, company_currency);
-		} else {
-			frm.set_value("exchange_rate", 1.0);
-			frm.set_df_property('exchange_rate', 'hidden', 1);
-			frm.set_df_property("exchange_rate", "description", "" );
-		}
-		frm.refresh_fields();
 	},
 
 	set_exchange_rate: function(frm, from_currency, company_currency) {
diff --git a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
index 4e9ee3b..336e13c 100644
--- a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
+++ b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
@@ -38,7 +38,8 @@
 		onboarding.insert()
 		onboarding.submit()
 
-		self.assertEqual(onboarding.project, 'Employee Onboarding : Test Researcher - test@researcher.com')
+		project_name = frappe.db.get_value("Project", onboarding.project, "project_name")
+		self.assertEqual(project_name, 'Employee Onboarding : Test Researcher - test@researcher.com')
 
 		# don't allow making employee if onboarding is not complete
 		self.assertRaises(IncompleteTaskError, make_employee, onboarding.name)
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js
index 221300b..629341f 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.js
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.js
@@ -2,11 +2,21 @@
 // License: GNU General Public License v3. See license.txt
 
 frappe.provide("erpnext.hr");
+frappe.provide("erpnext.accounts.dimensions");
 
-erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({
-	expense_type: function(doc, cdt, cdn) {
+frappe.ui.form.on('Expense Claim', {
+	onload: function(frm) {
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
+	},
+	company: function(frm) {
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
+	},
+});
+
+frappe.ui.form.on('Expense Claim Detail', {
+	expense_type: function(frm, cdt, cdn) {
 		var d = locals[cdt][cdn];
-		if(!doc.company) {
+		if (!frm.doc.company) {
 			d.expense_type = "";
 			frappe.msgprint(__("Please set the Company"));
 			this.frm.refresh_fields();
@@ -20,7 +30,7 @@
 			method: "erpnext.hr.doctype.expense_claim.expense_claim.get_expense_claim_account_and_cost_center",
 			args: {
 				"expense_claim_type": d.expense_type,
-				"company": doc.company
+				"company": frm.doc.company
 			},
 			callback: function(r) {
 				if (r.message) {
@@ -32,8 +42,6 @@
 	}
 });
 
-$.extend(cur_frm.cscript, new erpnext.hr.ExpenseClaimController({frm: cur_frm}));
-
 cur_frm.add_fetch('employee', 'company', 'company');
 cur_frm.add_fetch('employee','employee_name','employee_name');
 cur_frm.add_fetch('expense_type','description','description');
@@ -167,15 +175,6 @@
 			};
 		});
 
-		frm.set_query("cost_center", "expenses", function() {
-			return {
-				filters: {
-					"company": frm.doc.company,
-					"is_group": 0
-				}
-			};
-		});
-
 		frm.set_query("payable_account", function() {
 			return {
 				filters: {
diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
index 4a0908d..f9e3a44 100644
--- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
@@ -20,35 +20,36 @@
 		frappe.db.sql("""delete from `tabProject` where name = "_Test Project 1" """)
 		frappe.db.sql("update `tabExpense Claim` set project = '', task = ''")
 
-		frappe.get_doc({
+		project = frappe.get_doc({
 			"project_name": "_Test Project 1",
 			"doctype": "Project"
-		}).save()
+		})
+		project.save()
 
 		task = frappe.get_doc(dict(
 			doctype = 'Task',
 			subject = '_Test Project Task 1',
 			status = 'Open',
-			project = '_Test Project 1'
+			project = project.name
 		)).insert()
 
 		task_name = task.name
 		payable_account = get_payable_account(company_name)
 
-		make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", "_Test Project 1", task_name)
+		make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", project.name, task_name)
 
 		self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200)
-		self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
+		self.assertEqual(frappe.db.get_value("Project", project.name, "total_expense_claim"), 200)
 
-		expense_claim2 = make_expense_claim(payable_account, 600, 500, company_name, "Travel Expenses - _TC4","_Test Project 1", task_name)
+		expense_claim2 = make_expense_claim(payable_account, 600, 500, company_name, "Travel Expenses - _TC4", project.name, task_name)
 
 		self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700)
-		self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700)
+		self.assertEqual(frappe.db.get_value("Project", project.name, "total_expense_claim"), 700)
 
 		expense_claim2.cancel()
 
 		self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200)
-		self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
+		self.assertEqual(frappe.db.get_value("Project", project.name, "total_expense_claim"), 200)
 
 	def test_expense_claim_status(self):
 		payable_account = get_payable_account(company_name)
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.json b/erpnext/hr/doctype/job_applicant/job_applicant.json
index c13548a..1360fd1 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant.json
+++ b/erpnext/hr/doctype/job_applicant/job_applicant.json
@@ -11,15 +11,24 @@
  "field_order": [
   "applicant_name",
   "email_id",
+  "phone_number",
+  "country",
   "status",
   "column_break_3",
   "job_title",
   "source",
   "source_name",
+  "applicant_rating",
   "section_break_6",
   "notes",
   "cover_letter",
-  "resume_attachment"
+  "resume_attachment",
+  "resume_link",
+  "section_break_16",
+  "currency",
+  "column_break_18",
+  "lower_range",
+  "upper_range"
  ],
  "fields": [
   {
@@ -91,12 +100,65 @@
    "fieldtype": "Data",
    "label": "Notes",
    "read_only": 1
+  },
+  {
+   "fieldname": "phone_number",
+   "fieldtype": "Data",
+   "label": "Phone Number",
+   "options": "Phone"
+  },
+  {
+   "fieldname": "country",
+   "fieldtype": "Link",
+   "label": "Country",
+   "options": "Country"
+  },
+  {
+   "fieldname": "resume_link",
+   "fieldtype": "Data",
+   "label": "Resume Link"
+  },
+  {
+   "fieldname": "applicant_rating",
+   "fieldtype": "Rating",
+   "in_list_view": 1,
+   "label": "Applicant Rating"
+  },
+  {
+   "fieldname": "section_break_16",
+   "fieldtype": "Section Break",
+   "label": "Salary Expectation"
+  },
+  {
+   "fieldname": "lower_range",
+   "fieldtype": "Currency",
+   "label": "Lower Range",
+   "options": "currency",
+   "precision": "0"
+  },
+  {
+   "fieldname": "upper_range",
+   "fieldtype": "Currency",
+   "label": "Upper Range",
+   "options": "currency",
+   "precision": "0"
+  },
+  {
+   "fieldname": "column_break_18",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "currency",
+   "fieldtype": "Link",
+   "label": "Currency",
+   "options": "Currency"
   }
  ],
  "icon": "fa fa-user",
  "idx": 1,
+ "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-01-13 16:19:39.113330",
+ "modified": "2020-09-18 12:39:02.557563",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Job Applicant",
diff --git a/erpnext/hr/doctype/job_opening/job_opening.json b/erpnext/hr/doctype/job_opening/job_opening.json
index 4437e02..b8f6df6 100644
--- a/erpnext/hr/doctype/job_opening/job_opening.json
+++ b/erpnext/hr/doctype/job_opening/job_opening.json
@@ -1,456 +1,188 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "field:route", 
- "beta": 0, 
- "creation": "2013-01-15 16:13:36", 
- "custom": 0, 
- "description": "Description of a Job Opening", 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Document", 
- "editable_grid": 0, 
- "engine": "InnoDB", 
+ "actions": [],
+ "autoname": "field:route",
+ "creation": "2013-01-15 16:13:36",
+ "description": "Description of a Job Opening",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "engine": "InnoDB",
+ "field_order": [
+  "job_title",
+  "company",
+  "status",
+  "column_break_5",
+  "designation",
+  "department",
+  "staffing_plan",
+  "planned_vacancies",
+  "section_break_6",
+  "publish",
+  "route",
+  "column_break_12",
+  "job_application_route",
+  "section_break_14",
+  "description",
+  "section_break_16",
+  "currency",
+  "lower_range",
+  "upper_range",
+  "column_break_20",
+  "publish_salary_range"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "job_title", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Job Title", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "job_title",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Job Title",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "company", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Company", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Company", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "status", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 1, 
-   "label": "Status", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Open\nClosed", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Status",
+   "options": "Open\nClosed"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_5", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_5",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "designation", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Designation", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Designation", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "designation",
+   "fieldtype": "Link",
+   "label": "Designation",
+   "options": "Designation",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "department", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Department", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Department", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "department",
+   "fieldtype": "Link",
+   "label": "Department",
+   "options": "Department"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "staffing_plan", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Staffing Plan", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Staffing Plan", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "staffing_plan",
+   "fieldtype": "Link",
+   "label": "Staffing Plan",
+   "options": "Staffing Plan",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "staffing_plan", 
-   "fieldname": "planned_vacancies", 
-   "fieldtype": "Int", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Planned number of Positions", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "staffing_plan",
+   "fieldname": "planned_vacancies",
+   "fieldtype": "Int",
+   "label": "Planned number of Positions",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_6", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_6",
+   "fieldtype": "Section Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "publish", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Publish on website", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "default": "0",
+   "fieldname": "publish",
+   "fieldtype": "Check",
+   "label": "Publish on website"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "publish", 
-   "fieldname": "route", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Route", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
+   "depends_on": "publish",
+   "fieldname": "route",
+   "fieldtype": "Data",
+   "label": "Route",
    "unique": 1
-  }, 
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "Job profile, qualifications required etc.", 
-   "fieldname": "description", 
-   "fieldtype": "Text Editor", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Description", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
+   "description": "Job profile, qualifications required etc.",
+   "fieldname": "description",
+   "fieldtype": "Text Editor",
+   "in_list_view": 1,
+   "label": "Description"
+  },
+  {
+   "fieldname": "column_break_12",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "section_break_14",
+   "fieldtype": "Section Break"
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "section_break_16",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "currency",
+   "fieldtype": "Link",
+   "label": "Currency",
+   "options": "Currency"
+  },
+  {
+   "fieldname": "lower_range",
+   "fieldtype": "Currency",
+   "label": "Lower Range",
+   "options": "currency",
+   "precision": "0"
+  },
+  {
+   "fieldname": "upper_range",
+   "fieldtype": "Currency",
+   "label": "Upper Range",
+   "options": "currency",
+   "precision": "0"
+  },
+  {
+   "fieldname": "column_break_20",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "publish",
+   "description": "Route to the custom Job Application Webform",
+   "fieldname": "job_application_route",
+   "fieldtype": "Data",
+   "label": "Job Application Route"
+  },
+  {
+   "default": "0",
+   "fieldname": "publish_salary_range",
+   "fieldtype": "Check",
+   "label": "Publish Salary Range"
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "icon": "fa fa-bookmark", 
- "idx": 1, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-05-20 15:38:44.705823", 
- "modified_by": "Administrator", 
- "module": "HR", 
- "name": "Job Opening", 
- "owner": "Administrator", 
+ ],
+ "icon": "fa fa-bookmark",
+ "idx": 1,
+ "links": [],
+ "modified": "2020-09-18 11:23:29.488923",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Job Opening",
+ "owner": "Administrator",
  "permissions": [
   {
-   "amend": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 0, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "HR User", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
    "write": 1
-  }, 
+  },
   {
-   "amend": 0, 
-   "cancel": 0, 
-   "create": 0, 
-   "delete": 0, 
-   "email": 0, 
-   "export": 0, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 0, 
-   "read": 1, 
-   "report": 0, 
-   "role": "Guest", 
-   "set_user_permissions": 0, 
-   "share": 0, 
-   "submit": 0, 
-   "write": 0
+   "read": 1,
+   "role": "Guest"
   }
- ], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_order": "ASC", 
- "track_changes": 0, 
- "track_seen": 0
+ ],
+ "sort_field": "modified",
+ "sort_order": "ASC"
 }
\ No newline at end of file
diff --git a/erpnext/hr/doctype/job_opening/job_opening.py b/erpnext/hr/doctype/job_opening/job_opening.py
index 00883d7..1e89767 100644
--- a/erpnext/hr/doctype/job_opening/job_opening.py
+++ b/erpnext/hr/doctype/job_opening/job_opening.py
@@ -43,9 +43,8 @@
 			current_count = designation_counts['employee_count'] + designation_counts['job_openings']
 
 			if self.planned_vacancies <= current_count:
-				frappe.throw(_("Job Openings for designation {0} already open \
-					or hiring completed as per Staffing Plan {1}"
-					.format(self.designation, self.staffing_plan)))
+				frappe.throw(_("Job Openings for designation {0} already open or hiring completed as per Staffing Plan {1}").format(
+					self.designation, self.staffing_plan))
 
 	def get_context(self, context):
 		context.parents = [{'route': 'jobs', 'title': _('All Jobs') }]
@@ -56,7 +55,8 @@
 	context.get_list = get_job_openings
 
 def get_job_openings(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by=None):
-	fields = ['name', 'status', 'job_title', 'description']
+	fields = ['name', 'status', 'job_title', 'description', 'publish_salary_range',
+				'lower_range', 'upper_range', 'currency', 'job_application_route']
 
 	filters = filters or {}
 	filters.update({
diff --git a/erpnext/hr/doctype/job_opening/templates/job_opening_row.html b/erpnext/hr/doctype/job_opening/templates/job_opening_row.html
index 5da8cc8..c015101 100644
--- a/erpnext/hr/doctype/job_opening/templates/job_opening_row.html
+++ b/erpnext/hr/doctype/job_opening/templates/job_opening_row.html
@@ -1,9 +1,18 @@
 <div class="my-5">
 	<h3>{{ doc.job_title }}</h3>
 	<p>{{ doc.description }}</p>
+	{%- if doc.publish_salary_range -%} 
+	<p><b>{{_("Salary range per month")}}: </b>{{ frappe.format_value(frappe.utils.flt(doc.lower_range), currency=doc.currency) }} - {{ frappe.format_value(frappe.utils.flt(doc.upper_range), currency=doc.currency) }}</p>
+	{% endif %}
 	<div>
-		<a class="btn btn-primary"
-		href="/job_application?new=1&job_title={{ doc.name }}">
+		{%- if doc.job_application_route -%}
+		<a class='btn btn-primary' 
+		href='/{{doc.job_application_route}}?new=1&job_title={{ doc.name }}'>
 		{{ _("Apply Now") }}</a>
+		{% else %}
+		<a class='btn btn-primary' 
+		href='/job_application?new=1&job_title={{ doc.name }}'>
+		{{ _("Apply Now") }}</a>
+		{% endif %}
 	</div>
 </div>
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json
index 4b31501..3a300c0 100644
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json
@@ -11,6 +11,7 @@
   "employee",
   "employee_name",
   "department",
+  "company",
   "column_break1",
   "leave_type",
   "from_date",
@@ -219,6 +220,15 @@
    "label": "Leave Policy Assignment",
    "options": "Leave Policy Assignment",
    "read_only": 1
+  },
+  {
+   "fetch_from": "employee.company",
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "read_only": 1,
+   "reqd": 1
   }
  ],
  "icon": "fa fa-ok",
@@ -226,7 +236,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-08-20 14:25:10.314323",
+ "modified": "2021-01-04 18:46:13.184104",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Leave Allocation",
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
index a09cd2e..a4f4f1a 100755
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
@@ -18,7 +18,6 @@
 class LeaveAllocation(Document):
 	def validate(self):
 		self.validate_period()
-		self.validate_new_leaves_allocated_value()
 		self.validate_allocation_overlap()
 		self.validate_back_dated_allocation()
 		self.set_total_leaves_allocated()
@@ -72,11 +71,6 @@
 		if frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"):
 			frappe.throw(_("Leave Type {0} cannot be allocated since it is leave without pay").format(self.leave_type))
 
-	def validate_new_leaves_allocated_value(self):
-		"""validate that leave allocation is in multiples of 0.5"""
-		if flt(self.new_leaves_allocated) % 0.5:
-			frappe.throw(_("Leaves must be allocated in multiples of 0.5"), ValueMultiplierError)
-
 	def validate_allocation_overlap(self):
 		leave_allocation = frappe.db.sql("""
 			SELECT
diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json
index 4abba5f..d74760a 100644
--- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json
+++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "creation": "2019-05-09 15:47:39.760406",
  "doctype": "DocType",
  "engine": "InnoDB",
@@ -8,6 +9,7 @@
   "leave_type",
   "transaction_type",
   "transaction_name",
+  "company",
   "leaves",
   "column_break_7",
   "from_date",
@@ -106,12 +108,22 @@
    "fieldtype": "Link",
    "label": "Holiday List",
    "options": "Holiday List"
+  },
+  {
+   "fetch_from": "employee.company",
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "read_only": 1,
+   "reqd": 1
   }
  ],
  "in_create": 1,
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
- "modified": "2020-09-04 12:16:36.569066",
+ "links": [],
+ "modified": "2021-01-04 18:47:45.146652",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Leave Ledger Entry",
diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json
index bbb4222..3373350 100644
--- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json
+++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json
@@ -106,18 +106,21 @@
    "fieldname": "leaves_allocated",
    "fieldtype": "Check",
    "hidden": 1,
-   "label": "Leaves Allocated"
+   "label": "Leaves Allocated",
+   "no_copy": 1,
+   "print_hide": 1
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2020-12-17 16:27:20.311060",
+ "modified": "2021-03-01 17:54:01.014509",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Leave Policy Assignment",
  "owner": "Administrator",
  "permissions": [
   {
+   "cancel": 1,
    "create": 1,
    "delete": 1,
    "email": 1,
@@ -131,6 +134,7 @@
    "write": 1
   },
   {
+   "cancel": 1,
    "create": 1,
    "delete": 1,
    "email": 1,
@@ -144,6 +148,7 @@
    "write": 1
   },
   {
+   "cancel": 1,
    "create": 1,
    "delete": 1,
    "email": 1,
diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py
index a5068bc..4064c56 100644
--- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py
+++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py
@@ -6,7 +6,7 @@
 import frappe
 from frappe.model.document import Document
 from frappe import _, bold
-from frappe.utils import getdate, date_diff, comma_and, formatdate
+from frappe.utils import getdate, date_diff, comma_and, formatdate, get_datetime, flt
 from math import ceil
 import json
 from six import string_types
@@ -84,17 +84,52 @@
 		return allocation.name, new_leaves_allocated
 
 	def get_new_leaves(self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining):
+		from frappe.model.meta import get_field_precision
+		precision = get_field_precision(frappe.get_meta("Leave Allocation").get_field("new_leaves_allocated"))
+
+		# Earned Leaves and Compensatory Leaves are allocated by scheduler, initially allocate 0
+		if leave_type_details.get(leave_type).is_compensatory == 1:
+			new_leaves_allocated = 0
+
+		elif leave_type_details.get(leave_type).is_earned_leave == 1:
+			if self.assignment_based_on == "Leave Period":
+				new_leaves_allocated = self.get_leaves_for_passed_months(leave_type, new_leaves_allocated, leave_type_details, date_of_joining)
+			else:
+				new_leaves_allocated = 0
 		# Calculate leaves at pro-rata basis for employees joining after the beginning of the given leave period
-		if getdate(date_of_joining) > getdate(self.effective_from):
+		elif getdate(date_of_joining) > getdate(self.effective_from):
 			remaining_period = ((date_diff(self.effective_to, date_of_joining) + 1) / (date_diff(self.effective_to, self.effective_from) + 1))
 			new_leaves_allocated = ceil(new_leaves_allocated * remaining_period)
 
-		# Earned Leaves and Compensatory Leaves are allocated by scheduler, initially allocate 0
-		if leave_type_details.get(leave_type).is_earned_leave == 1 or leave_type_details.get(leave_type).is_compensatory == 1:
-			new_leaves_allocated = 0
+		return flt(new_leaves_allocated, precision)
+
+	def get_leaves_for_passed_months(self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining):
+		from erpnext.hr.utils import get_monthly_earned_leave
+
+		current_month = get_datetime().month
+		current_year = get_datetime().year
+
+		from_date = frappe.db.get_value("Leave Period", self.leave_period, "from_date")
+		if getdate(date_of_joining) > getdate(from_date):
+			from_date = date_of_joining
+
+		from_date_month = get_datetime(from_date).month
+		from_date_year = get_datetime(from_date).year
+
+		months_passed = 0
+		if current_year == from_date_year and current_month > from_date_month:
+			months_passed = current_month - from_date_month
+		elif current_year > from_date_year:
+			months_passed = (12 - from_date_month) + current_month
+
+		if months_passed > 0:
+			monthly_earned_leave = get_monthly_earned_leave(new_leaves_allocated,
+				leave_type_details.get(leave_type).earned_leave_frequency, leave_type_details.get(leave_type).rounding)
+			new_leaves_allocated = monthly_earned_leave * months_passed
 
 		return new_leaves_allocated
 
+
 @frappe.whitelist()
 def grant_leave_for_multiple_employees(leave_policy_assignments):
 	leave_policy_assignments = json.loads(leave_policy_assignments)
@@ -156,7 +191,8 @@
 def get_leave_type_details():
 	leave_type_details = frappe._dict()
 	leave_types = frappe.get_all("Leave Type",
-		fields=["name", "is_lwp", "is_earned_leave", "is_compensatory", "is_carry_forward", "expire_carry_forwarded_leaves_after_days"])
+		fields=["name", "is_lwp", "is_earned_leave", "is_compensatory",
+			"is_carry_forward", "expire_carry_forwarded_leaves_after_days", "earned_leave_frequency", "rounding"])
 	for d in leave_types:
 		leave_type_details.setdefault(d.name, d)
 	return leave_type_details
diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json
index a209291..fc577ef 100644
--- a/erpnext/hr/doctype/leave_type/leave_type.json
+++ b/erpnext/hr/doctype/leave_type/leave_type.json
@@ -172,7 +172,7 @@
    "fieldname": "rounding",
    "fieldtype": "Select",
    "label": "Rounding",
-   "options": "0.5\n1.0"
+   "options": "\n0.25\n0.5\n1.0"
   },
   {
    "depends_on": "is_carry_forward",
@@ -197,6 +197,7 @@
    "label": "Based On Date Of Joining"
   },
   {
+   "default": "0",
    "depends_on": "eval:doc.is_lwp == 0",
    "fieldname": "is_ppl",
    "fieldtype": "Check",
@@ -213,7 +214,7 @@
  "icon": "fa fa-flag",
  "idx": 1,
  "links": [],
- "modified": "2020-10-15 15:49:47.555105",
+ "modified": "2021-03-02 11:22:33.776320",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Leave Type",
diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.py b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
index 2c385e8..ab65260 100644
--- a/erpnext/hr/doctype/shift_assignment/shift_assignment.py
+++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
@@ -88,7 +88,7 @@
 
 def add_assignments(events, start, end, conditions=None):
 	query = """select name, start_date, end_date, employee_name,
-		employee, docstatus
+		employee, docstatus, shift_type
 		from `tabShift Assignment` where
 		start_date >= %(start_date)s
 		or end_date <=  %(end_date)s
@@ -97,18 +97,40 @@
 	if conditions:
 		query += conditions
 
-	for d in frappe.db.sql(query, {"start_date":start, "end_date":end}, as_dict=True):
-		e = {
-			"name": d.name,
-			"doctype": "Shift Assignment",
-			"start_date": d.start_date,
-			"end_date": d.end_date if d.end_date else nowdate(),
-			"title": cstr(d.employee_name) + ": "+ \
-				cstr(d.shift_type),
-			"docstatus": d.docstatus
-		}
-		if e not in events:
-			events.append(e)
+	records = frappe.db.sql(query, {"start_date":start, "end_date":end}, as_dict=True)
+	shift_timing_map = get_shift_type_timing([d.shift_type for d in records])
+
+	for d in records:
+		daily_event_start = d.start_date
+		daily_event_end = d.end_date if d.end_date else getdate()
+		delta = timedelta(days=1)
+		while daily_event_start <= daily_event_end:
+			start_timing = frappe.utils.get_datetime(daily_event_start)+ shift_timing_map[d.shift_type]['start_time']
+			end_timing = frappe.utils.get_datetime(daily_event_start)+ shift_timing_map[d.shift_type]['end_time']
+			daily_event_start += delta
+			e = {
+				"name": d.name,
+				"doctype": "Shift Assignment",
+				"start_date": start_timing,
+				"end_date": end_timing,
+				"title": cstr(d.employee_name) + ": "+ \
+					cstr(d.shift_type),
+				"docstatus": d.docstatus,
+				"allDay": 0
+			}
+			if e not in events:
+				events.append(e)
+
+	return events
+
+def get_shift_type_timing(shift_types):
+	shift_timing_map = {}
+	data = frappe.get_all("Shift Type", filters = {"name": ("IN", shift_types)}, fields = ['name', 'start_time', 'end_time'])
+
+	for d in data:
+		shift_timing_map[d.name] = d
+
+	return shift_timing_map
 
 
 def get_employee_shift(employee, for_date=nowdate(), consider_default_shift=False, next_shift_direction=None):
diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js b/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js
index 17a986d..bb692e1 100644
--- a/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js
+++ b/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js
@@ -6,14 +6,8 @@
 		"start": "start_date",
 		"end": "end_date",
 		"id": "name",
-		"docstatus": 1
-	},
-	options: {
-		header: {
-			left: 'prev,next today',
-			center: 'title',
-			right: 'month'
-		}
+		"docstatus": 1,
+		"allDay": "allDay",
 	},
 	get_events_method: "erpnext.hr.doctype.shift_assignment.shift_assignment.get_events"
 }
\ No newline at end of file
diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py
index d700e7f..3f8d61d 100644
--- a/erpnext/hr/utils.py
+++ b/erpnext/hr/utils.py
@@ -316,13 +316,7 @@
 				update_previous_leave_allocation(allocation, annual_allocation, e_leave_type)
 
 def update_previous_leave_allocation(allocation, annual_allocation, e_leave_type):
-	divide_by_frequency = {"Yearly": 1, "Half-Yearly": 6, "Quarterly": 4, "Monthly": 12}
-	if annual_allocation:
-		earned_leaves = flt(annual_allocation) / divide_by_frequency[e_leave_type.earned_leave_frequency]
-		if e_leave_type.rounding == "0.5":
-			earned_leaves = round(earned_leaves * 2) / 2
-		else:
-			earned_leaves = round(earned_leaves)
+		earned_leaves = get_monthly_earned_leave(annual_allocation, e_leave_type.earned_leave_frequency, e_leave_type.rounding)
 
 		allocation = frappe.get_doc('Leave Allocation', allocation.name)
 		new_allocation = flt(allocation.total_leaves_allocated) + flt(earned_leaves)
@@ -335,6 +329,21 @@
 			today_date = today()
 			create_additional_leave_ledger_entry(allocation, earned_leaves, today_date)
 
+def get_monthly_earned_leave(annual_leaves, frequency, rounding):
+	earned_leaves = 0.0
+	divide_by_frequency = {"Yearly": 1, "Half-Yearly": 6, "Quarterly": 4, "Monthly": 12}
+	if annual_leaves:
+		earned_leaves = flt(annual_leaves) / divide_by_frequency[frequency]
+		if rounding:
+			if rounding == "0.25":
+				earned_leaves = round(earned_leaves * 4) / 4
+			elif rounding == "0.5":
+				earned_leaves = round(earned_leaves * 2) / 2
+			else:
+				earned_leaves = round(earned_leaves)
+
+	return earned_leaves
+
 
 def get_leave_allocations(date, leave_type):
 	return frappe.db.sql("""select name, employee, from_date, to_date, leave_policy_assignment, leave_policy
diff --git a/erpnext/hr/web_form/job_application/job_application.json b/erpnext/hr/web_form/job_application/job_application.json
index f630570..512ba5c 100644
--- a/erpnext/hr/web_form/job_application/job_application.json
+++ b/erpnext/hr/web_form/job_application/job_application.json
@@ -1,86 +1,200 @@
 {
- "accept_payment": 0, 
- "allow_comments": 1, 
- "allow_delete": 0, 
- "allow_edit": 1, 
- "allow_incomplete": 0, 
- "allow_multiple": 1, 
- "allow_print": 0, 
- "amount": 0.0, 
- "amount_based_on_field": 0, 
- "creation": "2016-09-10 02:53:16.598314", 
- "doc_type": "Job Applicant", 
- "docstatus": 0, 
- "doctype": "Web Form", 
- "idx": 0, 
- "introduction_text": "", 
- "is_standard": 1, 
- "login_required": 0, 
- "max_attachment_size": 0, 
- "modified": "2016-12-20 00:21:44.081622", 
- "modified_by": "Administrator", 
- "module": "HR", 
- "name": "job-application", 
- "owner": "Administrator", 
- "published": 1, 
- "route": "job_application", 
- "show_sidebar": 1, 
- "sidebar_items": [], 
- "success_message": "Thank you for applying.", 
- "success_url": "/jobs", 
- "title": "Job Application", 
+ "accept_payment": 0,
+ "allow_comments": 1,
+ "allow_delete": 0,
+ "allow_edit": 1,
+ "allow_incomplete": 0,
+ "allow_multiple": 1,
+ "allow_print": 0,
+ "amount": 0.0,
+ "amount_based_on_field": 0,
+ "apply_document_permissions": 0,
+ "client_script": "frappe.web_form.on('resume_link', (field, value) => {\n    if (!frappe.utils.is_url(value)) {\n        frappe.msgprint(__('Resume link not valid'));\n    }\n});\n",
+ "creation": "2016-09-10 02:53:16.598314",
+ "doc_type": "Job Applicant",
+ "docstatus": 0,
+ "doctype": "Web Form",
+ "idx": 0,
+ "introduction_text": "",
+ "is_standard": 1,
+ "login_required": 0,
+ "max_attachment_size": 0,
+ "modified": "2020-10-07 19:27:17.143355",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "job-application",
+ "owner": "Administrator",
+ "published": 1,
+ "route": "job_application",
+ "route_to_success_link": 0,
+ "show_attachments": 0,
+ "show_in_grid": 0,
+ "show_sidebar": 1,
+ "sidebar_items": [],
+ "success_message": "Thank you for applying.",
+ "success_url": "/jobs",
+ "title": "Job Application",
  "web_form_fields": [
   {
-   "fieldname": "job_title", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "label": "Job Opening", 
-   "max_length": 0, 
-   "max_value": 0, 
-   "options": "", 
-   "read_only": 1, 
-   "reqd": 0
-  }, 
+   "allow_read_on_all_link_options": 0,
+   "fieldname": "job_title",
+   "fieldtype": "Data",
+   "hidden": 0,
+   "label": "Job Opening",
+   "max_length": 0,
+   "max_value": 0,
+   "options": "",
+   "read_only": 1,
+   "reqd": 0,
+   "show_in_filter": 0
+  },
   {
-   "fieldname": "applicant_name", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "label": "Applicant Name", 
-   "max_length": 0, 
-   "max_value": 0, 
-   "read_only": 0, 
-   "reqd": 1
-  }, 
+   "allow_read_on_all_link_options": 0,
+   "fieldname": "applicant_name",
+   "fieldtype": "Data",
+   "hidden": 0,
+   "label": "Applicant Name",
+   "max_length": 0,
+   "max_value": 0,
+   "read_only": 0,
+   "reqd": 1,
+   "show_in_filter": 0
+  },
   {
-   "fieldname": "email_id", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "label": "Email Address", 
-   "max_length": 0, 
-   "max_value": 0, 
-   "options": "Email", 
-   "read_only": 0, 
-   "reqd": 1
-  }, 
+   "allow_read_on_all_link_options": 0,
+   "fieldname": "email_id",
+   "fieldtype": "Data",
+   "hidden": 0,
+   "label": "Email Address",
+   "max_length": 0,
+   "max_value": 0,
+   "options": "Email",
+   "read_only": 0,
+   "reqd": 1,
+   "show_in_filter": 0
+  },
   {
-   "fieldname": "cover_letter", 
-   "fieldtype": "Text", 
-   "hidden": 0, 
-   "label": "Cover Letter", 
-   "max_length": 0, 
-   "max_value": 0, 
-   "read_only": 0, 
-   "reqd": 0
-  }, 
+   "allow_read_on_all_link_options": 0,
+   "fieldname": "phone_number",
+   "fieldtype": "Data",
+   "hidden": 0,
+   "label": "Phone Number",
+   "max_length": 0,
+   "max_value": 0,
+   "options": "Phone",
+   "read_only": 0,
+   "reqd": 0,
+   "show_in_filter": 0
+  },
   {
-   "fieldname": "resume_attachment", 
-   "fieldtype": "Attach", 
-   "hidden": 0, 
-   "label": "Resume Attachment", 
-   "max_length": 0, 
-   "max_value": 0, 
-   "read_only": 0, 
-   "reqd": 0
+   "allow_read_on_all_link_options": 0,
+   "fieldname": "country",
+   "fieldtype": "Link",
+   "hidden": 0,
+   "label": "Country of Residence",
+   "max_length": 0,
+   "max_value": 0,
+   "options": "Country",
+   "read_only": 0,
+   "reqd": 0,
+   "show_in_filter": 0
+  },
+  {
+   "allow_read_on_all_link_options": 0,
+   "fieldname": "cover_letter",
+   "fieldtype": "Text",
+   "hidden": 0,
+   "label": "Cover Letter",
+   "max_length": 0,
+   "max_value": 0,
+   "read_only": 0,
+   "reqd": 0,
+   "show_in_filter": 0
+  },
+  {
+   "allow_read_on_all_link_options": 0,
+   "fieldname": "resume_link",
+   "fieldtype": "Data",
+   "hidden": 0,
+   "label": "Resume Link",
+   "max_length": 0,
+   "max_value": 0,
+   "read_only": 0,
+   "reqd": 0,
+   "show_in_filter": 0
+  },
+  {
+   "allow_read_on_all_link_options": 0,
+   "fieldname": "",
+   "fieldtype": "Section Break",
+   "hidden": 0,
+   "label": "Expected Salary Range per month",
+   "max_length": 0,
+   "max_value": 0,
+   "read_only": 0,
+   "reqd": 1,
+   "show_in_filter": 0
+  },
+  {
+   "allow_read_on_all_link_options": 0,
+   "fieldname": "currency",
+   "fieldtype": "Link",
+   "hidden": 0,
+   "label": "Currency",
+   "max_length": 0,
+   "max_value": 0,
+   "options": "Currency",
+   "read_only": 0,
+   "reqd": 0,
+   "show_in_filter": 0
+  },
+  {
+   "allow_read_on_all_link_options": 0,
+   "fieldname": "",
+   "fieldtype": "Column Break",
+   "hidden": 0,
+   "max_length": 0,
+   "max_value": 0,
+   "read_only": 0,
+   "reqd": 0,
+   "show_in_filter": 0
+  },
+  {
+   "allow_read_on_all_link_options": 0,
+   "fieldname": "lower_range",
+   "fieldtype": "Currency",
+   "hidden": 0,
+   "label": "Lower Range",
+   "max_length": 0,
+   "max_value": 0,
+   "options": "currency",
+   "read_only": 0,
+   "reqd": 0,
+   "show_in_filter": 0
+  },
+  {
+   "allow_read_on_all_link_options": 0,
+   "fieldname": "",
+   "fieldtype": "Column Break",
+   "hidden": 0,
+   "max_length": 0,
+   "max_value": 0,
+   "read_only": 0,
+   "reqd": 0,
+   "show_in_filter": 0
+  },
+  {
+   "allow_read_on_all_link_options": 0,
+   "fieldname": "upper_range",
+   "fieldtype": "Currency",
+   "hidden": 0,
+   "label": "Upper Range",
+   "max_length": 0,
+   "max_value": 0,
+   "options": "currency",
+   "read_only": 0,
+   "reqd": 0,
+   "show_in_filter": 0
   }
  ]
 }
\ No newline at end of file
diff --git a/erpnext/loan_management/desk_page/loan/loan.json b/erpnext/loan_management/desk_page/loan/loan.json
index fc59c19..75036bd 100644
--- a/erpnext/loan_management/desk_page/loan/loan.json
+++ b/erpnext/loan_management/desk_page/loan/loan.json
@@ -23,7 +23,7 @@
   {
    "hidden": 0,
    "label": "Reports",
-   "links": "[\n    {\n        \"doctype\": \"Loan Repayment\",\n        \"is_query_report\": true,\n        \"label\": \"Loan Repayment and Closure\",\n        \"name\": \"Loan Repayment and Closure\",\n        \"route\": \"#query-report/Loan Repayment and Closure\",\n        \"type\": \"report\"\n    },\n    {\n        \"doctype\": \"Loan Security Pledge\",\n        \"is_query_report\": true,\n        \"label\": \"Loan Security Status\",\n        \"name\": \"Loan Security Status\",\n        \"route\": \"#query-report/Loan Security Status\",\n        \"type\": \"report\"\n    }\n]"
+   "links": "[\n    {\n        \"doctype\": \"Loan Repayment\",\n        \"is_query_report\": true,\n        \"label\": \"Loan Repayment and Closure\",\n        \"name\": \"Loan Repayment and Closure\",\n        \"route\": \"#query-report/Loan Repayment and Closure\",\n        \"type\": \"report\"\n    },\n    {\n        \"doctype\": \"Loan Security Pledge\",\n        \"is_query_report\": true,\n        \"label\": \"Loan Security Status\",\n        \"name\": \"Loan Security Status\",\n        \"route\": \"#query-report/Loan Security Status\",\n        \"type\": \"report\"\n    },\n    {\n        \"doctype\": \"Loan Interest Accrual\",\n        \"is_query_report\": true,\n        \"label\": \"Loan Interest Report\",\n        \"name\": \"Loan Interest Report\",\n        \"route\": \"#query-report/Loan Interest Report\",\n        \"type\": \"report\"\n    },\n    {\n        \"doctype\": \"Loan Security\",\n        \"is_query_report\": true,\n        \"label\": \"Loan Security Exposure\",\n        \"name\": \"Loan Security Exposure\",\n        \"route\": \"#query-report/Loan Security Exposure\",\n        \"type\": \"report\"\n    },\n    {\n        \"doctype\": \"Loan Security\",\n        \"is_query_report\": true,\n        \"label\": \"Applicant-Wise Loan Security Exposure\",\n        \"name\": \"Applicant-Wise Loan Security Exposure\",\n        \"route\": \"#query-report/Applicant-Wise Loan Security Exposure\",\n        \"type\": \"report\"\n    }\n]"
   }
  ],
  "category": "Modules",
@@ -38,7 +38,7 @@
  "idx": 0,
  "is_standard": 1,
  "label": "Loan",
- "modified": "2020-10-17 12:59:50.336085",
+ "modified": "2021-01-17 07:21:22.092184",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan",
diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py
index cd40a66..e607d4f 100644
--- a/erpnext/loan_management/doctype/loan/loan.py
+++ b/erpnext/loan_management/doctype/loan/loan.py
@@ -6,6 +6,7 @@
 import frappe, math, json
 import erpnext
 from frappe import _
+from six import string_types
 from frappe.utils import flt, rounded, add_months, nowdate, getdate, now_datetime
 from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import get_pledged_security_qty
 from erpnext.controllers.accounts_controller import AccountsController
@@ -201,7 +202,9 @@
 
 	# checking greater than 0 as there may be some minor precision error
 	if pending_amount < write_off_limit:
-		# update status as loan closure requested
+		# Auto create loan write off and update status as loan closure requested
+		write_off = make_loan_write_off(loan)
+		write_off.submit()
 		frappe.db.set_value('Loan', loan, 'status', 'Loan Closure Requested')
 	else:
 		frappe.throw(_("Cannot close loan as there is an outstanding of {0}").format(pending_amount))
@@ -280,10 +283,13 @@
 		return write_off
 
 @frappe.whitelist()
-def unpledge_security(loan=None, loan_security_pledge=None, as_dict=0, save=0, submit=0, approve=0):
-	# if loan is passed it will be considered as full unpledge
+def unpledge_security(loan=None, loan_security_pledge=None, security_map=None, as_dict=0, save=0, submit=0, approve=0):
+	# if no security_map is passed it will be considered as full unpledge
+	if security_map and isinstance(security_map, string_types):
+		security_map = json.loads(security_map)
+
 	if loan:
-		pledge_qty_map = get_pledged_security_qty(loan)
+		pledge_qty_map = security_map or get_pledged_security_qty(loan)
 		loan_doc = frappe.get_doc('Loan', loan)
 		unpledge_request = create_loan_security_unpledge(pledge_qty_map, loan_doc.name, loan_doc.company,
 			loan_doc.applicant_type, loan_doc.applicant)
@@ -332,13 +338,13 @@
 	return unpledge_request
 
 def validate_employee_currency_with_company_currency(applicant, company):
-		from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_employee_currency
-		if not applicant:
-			frappe.throw(_("Please select Applicant"))
-		if not company:
-			frappe.throw(_("Please select Company"))
-		employee_currency = get_employee_currency(applicant)
-		company_currency = erpnext.get_company_currency(company)
-		if employee_currency != company_currency:
-			frappe.throw(_("Loan cannot be repayed from salary for Employee {0} because salary is processed in currency {1}")
-				.format(applicant, employee_currency))
+	from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_employee_currency
+	if not applicant:
+		frappe.throw(_("Please select Applicant"))
+	if not company:
+		frappe.throw(_("Please select Company"))
+	employee_currency = get_employee_currency(applicant)
+	company_currency = erpnext.get_company_currency(company)
+	if employee_currency != company_currency:
+		frappe.throw(_("Loan cannot be repayed from salary for Employee {0} because salary is processed in currency {1}")
+			.format(applicant, employee_currency))
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index a63d065..f3c9db6 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -45,7 +45,7 @@
 		create_loan_security_price("Test Security 2", 250, "Nos", get_datetime() , get_datetime(add_to_date(nowdate(), hours=24)))
 
 		self.applicant1 = make_employee("robert_loan@loan.com")
-		make_salary_structure("Test Salary Structure Loan", "Monthly", employee=self.applicant1, currency='INR')
+		make_salary_structure("Test Salary Structure Loan", "Monthly", employee=self.applicant1, currency='INR', company="_Test Company")
 		if not frappe.db.exists("Customer", "_Test Loan Customer"):
 			frappe.get_doc(get_customer_dict('_Test Loan Customer')).insert(ignore_permissions=True)
 
@@ -321,10 +321,68 @@
 		self.assertEquals(sum(pledged_qty.values()), 0)
 
 		amounts = amounts = calculate_amounts(loan.name, add_days(last_date, 5))
-		self.assertTrue(amounts['pending_principal_amount'] < 0)
+		self.assertEqual(amounts['pending_principal_amount'], 0)
 		self.assertEquals(amounts['payable_principal_amount'], 0.0)
 		self.assertEqual(amounts['interest_amount'], 0)
 
+	def test_partial_loan_security_unpledge(self):
+		pledge = [{
+			"loan_security": "Test Security 1",
+			"qty": 2000.00
+		},
+		{
+			"loan_security": "Test Security 2",
+			"qty": 4000.00
+		}]
+
+		loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+		create_pledge(loan_application)
+
+		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+		loan.submit()
+
+		self.assertEquals(loan.loan_amount, 1000000)
+
+		first_date = '2019-10-01'
+		last_date = '2019-10-30'
+
+		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
+		process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+
+		repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5), 600000)
+		repayment_entry.submit()
+
+		unpledge_map = {'Test Security 2': 2000}
+
+		unpledge_request = unpledge_security(loan=loan.name, security_map = unpledge_map, save=1)
+		unpledge_request.submit()
+		unpledge_request.status = 'Approved'
+		unpledge_request.save()
+		unpledge_request.submit()
+		unpledge_request.load_from_db()
+		self.assertEqual(unpledge_request.docstatus, 1)
+
+	def test_santined_loan_security_unpledge(self):
+		pledge = [{
+			"loan_security": "Test Security 1",
+			"qty": 4000.00
+		}]
+
+		loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+		create_pledge(loan_application)
+
+		loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+		loan.submit()
+
+		self.assertEquals(loan.loan_amount, 1000000)
+
+		unpledge_map = {'Test Security 1': 4000}
+		unpledge_request = unpledge_security(loan=loan.name, security_map = unpledge_map, save=1)
+		unpledge_request.submit()
+		unpledge_request.status = 'Approved'
+		unpledge_request.save()
+		unpledge_request.submit()
+
 	def test_disbursal_check_with_shortfall(self):
 		pledges = [{
 			"loan_security": "Test Security 2",
@@ -415,7 +473,7 @@
 		self.assertEquals(loan.status, "Loan Closure Requested")
 
 		amounts = calculate_amounts(loan.name, add_days(last_date, 5))
-		self.assertTrue(amounts['pending_principal_amount'] < 0.0)
+		self.assertEqual(amounts['pending_principal_amount'], 0.0)
 
 	def test_partial_unaccrued_interest_payment(self):
 		pledge = [{
diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json
index f157f0d..185bf7a 100644
--- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json
+++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json
@@ -22,6 +22,7 @@
   "paid_principal_amount",
   "column_break_14",
   "interest_amount",
+  "total_pending_interest_amount",
   "paid_interest_amount",
   "penalty_amount",
   "section_break_15",
@@ -172,13 +173,19 @@
    "hidden": 1,
    "label": "Last Accrual Date",
    "read_only": 1
+  },
+  {
+   "fieldname": "total_pending_interest_amount",
+   "fieldtype": "Currency",
+   "label": "Total Pending Interest Amount",
+   "options": "Company:company:default_currency"
   }
  ],
  "in_create": 1,
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-11-07 05:49:25.448875",
+ "modified": "2021-01-10 00:15:21.544140",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan Interest Accrual",
diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
index d17f5af..7d7992d 100644
--- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
@@ -100,6 +100,8 @@
 	interest_per_day = get_per_day_interest(pending_principal_amount, loan.rate_of_interest, posting_date)
 	payable_interest = interest_per_day * no_of_days
 
+	pending_amounts = calculate_amounts(loan.name, posting_date, payment_type='Loan Closure')
+
 	args = frappe._dict({
 		'loan': loan.name,
 		'applicant_type': loan.applicant_type,
@@ -108,7 +110,8 @@
 		'loan_account': loan.loan_account,
 		'pending_principal_amount': pending_principal_amount,
 		'interest_amount': payable_interest,
-		'penalty_amount': calculate_amounts(loan.name, posting_date)['penalty_amount'],
+		'total_pending_interest_amount': pending_amounts['interest_amount'],
+		'penalty_amount': pending_amounts['penalty_amount'],
 		'process_loan_interest': process_loan_interest,
 		'posting_date': posting_date,
 		'accrual_type': accrual_type
@@ -202,6 +205,7 @@
 	loan_interest_accrual.loan_account = args.loan_account
 	loan_interest_accrual.pending_principal_amount = flt(args.pending_principal_amount, precision)
 	loan_interest_accrual.interest_amount = flt(args.interest_amount, precision)
+	loan_interest_accrual.total_pending_interest_amount = flt(args.total_pending_interest_amount, precision)
 	loan_interest_accrual.penalty_amount = flt(args.penalty_amount, precision)
 	loan_interest_accrual.posting_date = args.posting_date or nowdate()
 	loan_interest_accrual.process_loan_interest_accrual = args.process_loan_interest
diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py
index 46a6440..85e008a 100644
--- a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py
+++ b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py
@@ -37,10 +37,8 @@
 
 		loan_application = create_loan_application('_Test Company', self.applicant, 'Demand Loan', pledge)
 		create_pledge(loan_application)
-
 		loan = create_demand_loan(self.applicant, "Demand Loan", loan_application,
 			posting_date=get_first_day(nowdate()))
-
 		loan.submit()
 
 		first_date = '2019-10-01'
@@ -50,11 +48,46 @@
 
 		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
 			/ (days_in_year(get_datetime(first_date).year) * 100)
-
 		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
-
 		process_loan_interest_accrual_for_demand_loans(posting_date=last_date)
-
 		loan_interest_accural = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name})
 
 		self.assertEquals(flt(loan_interest_accural.interest_amount, 0), flt(accrued_interest_amount, 0))
+
+	def test_accumulated_amounts(self):
+		pledge = [{
+			"loan_security": "Test Security 1",
+			"qty": 4000.00
+		}]
+
+		loan_application = create_loan_application('_Test Company', self.applicant, 'Demand Loan', pledge)
+		create_pledge(loan_application)
+		loan = create_demand_loan(self.applicant, "Demand Loan", loan_application,
+			posting_date=get_first_day(nowdate()))
+		loan.submit()
+
+		first_date = '2019-10-01'
+		last_date = '2019-10-30'
+
+		no_of_days = date_diff(last_date, first_date) + 1
+		accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
+			/ (days_in_year(get_datetime(first_date).year) * 100)
+		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
+		process_loan_interest_accrual_for_demand_loans(posting_date=last_date)
+		loan_interest_accrual = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name})
+
+		self.assertEquals(flt(loan_interest_accrual.interest_amount, 0), flt(accrued_interest_amount, 0))
+
+		next_start_date = '2019-10-31'
+		next_end_date = '2019-11-29'
+
+		no_of_days = date_diff(next_end_date, next_start_date) + 1
+		process = process_loan_interest_accrual_for_demand_loans(posting_date=next_end_date)
+		new_accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
+			/ (days_in_year(get_datetime(first_date).year) * 100)
+
+		total_pending_interest_amount = flt(accrued_interest_amount + new_accrued_interest_amount, 0)
+
+		loan_interest_accrual = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name,
+			'process_loan_interest_accrual': process})
+		self.assertEquals(flt(loan_interest_accrual.total_pending_interest_amount, 0), total_pending_interest_amount)
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 415ba99..ac30c91 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -377,7 +377,7 @@
 	amounts["penalty_amount"] = flt(penalty_amount, precision)
 	amounts["payable_amount"] = flt(payable_principal_amount + total_pending_interest + penalty_amount, precision)
 	amounts["pending_accrual_entries"] = pending_accrual_entries
-	amounts["unaccrued_interest"] = unaccrued_interest
+	amounts["unaccrued_interest"] = flt(unaccrued_interest, precision)
 
 	if final_due_date:
 		amounts["due_date"] = final_due_date
diff --git a/erpnext/loan_management/doctype/loan_security_price/loan_security_price.json b/erpnext/loan_management/doctype/loan_security_price/loan_security_price.json
index a55b482..b6e8763 100644
--- a/erpnext/loan_management/doctype/loan_security_price/loan_security_price.json
+++ b/erpnext/loan_management/doctype/loan_security_price/loan_security_price.json
@@ -7,6 +7,7 @@
  "engine": "InnoDB",
  "field_order": [
   "loan_security",
+  "loan_security_name",
   "loan_security_type",
   "column_break_2",
   "uom",
@@ -79,10 +80,18 @@
    "label": "Loan Security Type",
    "options": "Loan Security Type",
    "read_only": 1
+  },
+  {
+   "fetch_from": "loan_security.loan_security_name",
+   "fieldname": "loan_security_name",
+   "fieldtype": "Data",
+   "label": "Loan Security Name",
+   "read_only": 1
   }
  ],
+ "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-06-11 03:41:33.900340",
+ "modified": "2021-01-17 07:41:49.598086",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan Security Price",
diff --git a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
index 8ec0bfb..6469806 100644
--- a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
+++ b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
@@ -81,7 +81,6 @@
 				process_loan_security_shortfall)
 
 def create_loan_security_shortfall(loan, loan_amount, security_value, shortfall_amount, process_loan_security_shortfall):
-
 	existing_shortfall = frappe.db.get_value("Loan Security Shortfall", {"loan": loan, "status": "Pending"}, "name")
 
 	if existing_shortfall:
diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
index c29f325..c4c2d68 100644
--- a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
+++ b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
@@ -30,6 +30,8 @@
 					d.idx, frappe.bold(d.loan_security)))
 
 	def validate_unpledge_qty(self):
+		from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import get_ltv_ratio
+
 		pledge_qty_map = get_pledged_security_qty(self.loan)
 
 		ltv_ratio_map = frappe._dict(frappe.get_all("Loan Security Type",
@@ -42,11 +44,19 @@
 				"valid_upto": (">=", get_datetime())
 			}, as_list=1))
 
-		total_payment, principal_paid, interest_payable, written_off_amount = frappe.get_value("Loan", self.loan, ['total_payment', 'total_principal_paid',
-			'total_interest_payable', 'written_off_amount'])
+		loan_details = frappe.get_value("Loan", self.loan, ['total_payment', 'total_principal_paid',
+			'total_interest_payable', 'written_off_amount', 'disbursed_amount', 'status'], as_dict=1)
 
-		pending_principal_amount = flt(total_payment) - flt(interest_payable) - flt(principal_paid) - flt(written_off_amount)
+		if loan_details.status == 'Disbursed':
+			pending_principal_amount = flt(loan_details.total_payment) - flt(loan_details.total_interest_payable) \
+				- flt(loan_details.total_principal_paid) - flt(loan_details.written_off_amount)
+		else:
+			pending_principal_amount = flt(loan_details.disbursed_amount) - flt(loan_details.total_interest_payable) \
+				- flt(loan_details.total_principal_paid) - flt(loan_details.written_off_amount)
+
 		security_value = 0
+		unpledge_qty_map = {}
+		ltv_ratio = 0
 
 		for security in self.securities:
 			pledged_qty = pledge_qty_map.get(security.loan_security, 0)
@@ -57,13 +67,15 @@
 				msg += _("You are trying to unpledge more.")
 				frappe.throw(msg, title=_("Loan Security Unpledge Error"))
 
-			qty_after_unpledge = pledged_qty - security.qty
-			ltv_ratio = ltv_ratio_map.get(security.loan_security_type)
+			unpledge_qty_map.setdefault(security.loan_security, 0)
+			unpledge_qty_map[security.loan_security] += security.qty
 
-			current_price = loan_security_price_map.get(security.loan_security)
-			if not current_price:
-				frappe.throw(_("No valid Loan Security Price found for {0}").format(frappe.bold(security.loan_security)))
+		for security in pledge_qty_map:
+			if not ltv_ratio:
+				ltv_ratio = get_ltv_ratio(security)
 
+			qty_after_unpledge = pledge_qty_map.get(security, 0) - unpledge_qty_map.get(security, 0)
+			current_price = loan_security_price_map.get(security)
 			security_value += qty_after_unpledge * current_price
 
 		if not security_value and flt(pending_principal_amount, 2) > 0:
diff --git a/erpnext/loan_management/doctype/loan_type/loan_type.json b/erpnext/loan_management/doctype/loan_type/loan_type.json
index 18a9731..3ef5304 100644
--- a/erpnext/loan_management/doctype/loan_type/loan_type.json
+++ b/erpnext/loan_management/doctype/loan_type/loan_type.json
@@ -144,17 +144,17 @@
   },
   {
    "allow_on_submit": 1,
-   "description": "Pending amount that will be automatically ignored on loan closure request ",
+   "description": "Loan Write Off will be automatically created on loan closure request if pending amount is below this limit",
    "fieldname": "write_off_amount",
    "fieldtype": "Currency",
-   "label": "Write Off Amount ",
+   "label": "Auto Write Off Amount ",
    "options": "Company:company:default_currency"
   }
  ],
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-10-26 07:13:55.029811",
+ "modified": "2021-01-17 06:51:26.082879",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan Type",
diff --git a/erpnext/loan_management/doctype/pledge/pledge.json b/erpnext/loan_management/doctype/pledge/pledge.json
index 801e3a3..c23479c 100644
--- a/erpnext/loan_management/doctype/pledge/pledge.json
+++ b/erpnext/loan_management/doctype/pledge/pledge.json
@@ -6,6 +6,7 @@
  "engine": "InnoDB",
  "field_order": [
   "loan_security",
+  "loan_security_name",
   "loan_security_type",
   "loan_security_code",
   "uom",
@@ -85,11 +86,18 @@
    "label": "Post Haircut Amount",
    "options": "Company:company:default_currency",
    "read_only": 1
+  },
+  {
+   "fetch_from": "loan_security.loan_security_name",
+   "fieldname": "loan_security_name",
+   "fieldtype": "Data",
+   "label": "Loan Security Name",
+   "read_only": 1
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2020-11-05 10:07:15.424937",
+ "modified": "2021-01-17 07:41:12.452514",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Pledge",
diff --git a/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.json b/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.json
index ffc3671..3feb305 100644
--- a/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.json
+++ b/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.json
@@ -30,7 +30,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2020-02-01 08:14:05.845161",
+ "modified": "2021-01-17 03:59:14.494557",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Process Loan Security Shortfall",
@@ -45,7 +45,9 @@
    "read": 1,
    "report": 1,
    "role": "System Manager",
+   "select": 1,
    "share": 1,
+   "submit": 1,
    "write": 1
   },
   {
@@ -57,7 +59,9 @@
    "read": 1,
    "report": 1,
    "role": "Loan Manager",
+   "select": 1,
    "share": 1,
+   "submit": 1,
    "write": 1
   }
  ],
diff --git a/erpnext/loan_management/doctype/proposed_pledge/proposed_pledge.json b/erpnext/loan_management/doctype/proposed_pledge/proposed_pledge.json
index 3e7e778..a0b3a79 100644
--- a/erpnext/loan_management/doctype/proposed_pledge/proposed_pledge.json
+++ b/erpnext/loan_management/doctype/proposed_pledge/proposed_pledge.json
@@ -6,6 +6,7 @@
  "engine": "InnoDB",
  "field_order": [
   "loan_security",
+  "loan_security_name",
   "qty",
   "loan_security_price",
   "amount",
@@ -56,12 +57,19 @@
    "label": "Post Haircut Amount",
    "options": "Company:company:default_currency",
    "read_only": 1
+  },
+  {
+   "fetch_from": "loan_security.loan_security_name",
+   "fieldname": "loan_security_name",
+   "fieldtype": "Data",
+   "label": "Loan Security Name",
+   "read_only": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-11-05 10:07:37.542344",
+ "modified": "2021-01-17 07:29:01.671722",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Proposed Pledge",
diff --git a/erpnext/loan_management/doctype/unpledge/unpledge.json b/erpnext/loan_management/doctype/unpledge/unpledge.json
index 0035668..0091e6c 100644
--- a/erpnext/loan_management/doctype/unpledge/unpledge.json
+++ b/erpnext/loan_management/doctype/unpledge/unpledge.json
@@ -6,6 +6,7 @@
  "engine": "InnoDB",
  "field_order": [
   "loan_security",
+  "loan_security_name",
   "loan_security_type",
   "loan_security_code",
   "haircut",
@@ -61,12 +62,19 @@
    "fieldtype": "Percent",
    "label": "Haircut",
    "read_only": 1
+  },
+  {
+   "fetch_from": "loan_security.loan_security_name",
+   "fieldname": "loan_security_name",
+   "fieldtype": "Data",
+   "label": "Loan Security Name",
+   "read_only": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-11-05 10:07:28.106961",
+ "modified": "2021-01-17 07:36:20.212342",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Unpledge",
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/loan_management/report/applicant_wise_loan_security_exposure/__init__.py
diff --git a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.js b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.js
new file mode 100644
index 0000000..73d60c4
--- /dev/null
+++ b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.js
@@ -0,0 +1,16 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Applicant-Wise Loan Security Exposure"] = {
+	"filters": [
+		{
+			"fieldname":"company",
+			"label": __("Company"),
+			"fieldtype": "Link",
+			"options": "Company",
+			"default": frappe.defaults.get_user_default("Company"),
+			"reqd": 1
+		}
+	]
+};
diff --git a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.json b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.json
new file mode 100644
index 0000000..a778cd7
--- /dev/null
+++ b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.json
@@ -0,0 +1,29 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-01-15 23:48:38.913514",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2021-01-15 23:48:38.913514",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Applicant-Wise Loan Security Exposure",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Loan Security",
+ "report_name": "Applicant-Wise Loan Security Exposure",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "System Manager"
+  },
+  {
+   "role": "Loan Manager"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py
new file mode 100644
index 0000000..ab586bc
--- /dev/null
+++ b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py
@@ -0,0 +1,139 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+import erpnext
+from frappe import _
+from frappe.utils import get_datetime, flt
+from six import iteritems
+
+def execute(filters=None):
+	columns = get_columns(filters)
+	data = get_data(filters)
+	return columns, data
+
+
+def get_columns(filters):
+	columns = [
+		{"label": _("Applicant Type"), "fieldname": "applicant_type", "options": "DocType", "width": 100},
+		{"label": _("Applicant Name"), "fieldname": "applicant_name", "fieldtype": "Dynamic Link", "options": "applicant_type", "width": 150},
+		{"label": _("Loan Security"), "fieldname": "loan_security", "fieldtype": "Link", "options": "Loan Security", "width": 160},
+		{"label": _("Loan Security Code"), "fieldname": "loan_security_code", "fieldtype": "Data", "width": 100},
+		{"label": _("Loan Security Name"), "fieldname": "loan_security_name", "fieldtype": "Data", "width": 150},
+		{"label": _("Haircut"), "fieldname": "haircut", "fieldtype": "Percent", "width": 100},
+		{"label": _("Loan Security Type"), "fieldname": "loan_security_type", "fieldtype": "Link", "options": "Loan Security Type", "width": 120},
+		{"label": _("Disabled"), "fieldname": "disabled", "fieldtype": "Check", "width": 80},
+		{"label": _("Total Qty"), "fieldname": "total_qty", "fieldtype": "Float", "width": 100},
+		{"label": _("Latest Price"), "fieldname": "latest_price", "fieldtype": "Currency", "options": "currency", "width": 100},
+		{"label": _("Price Valid Upto"), "fieldname": "price_valid_upto", "fieldtype": "Datetime", "width": 100},
+		{"label": _("Current Value"), "fieldname": "current_value", "fieldtype": "Currency", "options": "currency", "width": 100},
+		{"label": _("% Of Applicant Portfolio"), "fieldname": "portfolio_percent", "fieldtype": "Percentage", "width": 100},
+		{"label": _("Currency"), "fieldname": "currency", "fieldtype": "Currency", "options": "Currency", "hidden": 1, "width": 100},
+	]
+
+	return columns
+
+def get_data(filters):
+	data = []
+	loan_security_details = get_loan_security_details(filters)
+	pledge_values, total_value_map, applicant_type_map = get_applicant_wise_total_loan_security_qty(filters,
+		loan_security_details)
+
+	currency = erpnext.get_company_currency(filters.get('company'))
+
+	for key, qty in iteritems(pledge_values):
+		if qty:
+			row = {}
+			current_value = flt(qty * loan_security_details.get(key[1], {}).get('latest_price', 0))
+			valid_upto = loan_security_details.get(key[1], {}).get('valid_upto')
+
+			row.update(loan_security_details.get(key[1]))
+			row.update({
+				'applicant_type': applicant_type_map.get(key[0]),
+				'applicant_name': key[0],
+				'total_qty': qty,
+				'current_value': current_value,
+				'price_valid_upto': valid_upto,
+				'portfolio_percent': flt(current_value * 100 / total_value_map.get(key[0]), 2) if total_value_map.get(key[0]) \
+					else 0.0,
+				'currency': currency
+			})
+
+			data.append(row)
+
+	return data
+
+def get_loan_security_details(filters):
+	security_detail_map = {}
+	loan_security_price_map = {}
+	lsp_validity_map = {}
+
+	loan_security_prices = frappe.db.sql("""
+		SELECT loan_security, loan_security_price, valid_upto
+		FROM `tabLoan Security Price` t1
+		WHERE valid_from >= (SELECT MAX(valid_from) FROM `tabLoan Security Price` t2
+		WHERE t1.loan_security = t2.loan_security)
+	""", as_dict=1)
+
+	for security in loan_security_prices:
+		loan_security_price_map.setdefault(security.loan_security, security.loan_security_price)
+		lsp_validity_map.setdefault(security.loan_security, security.valid_upto)
+
+	loan_security_details = frappe.get_all('Loan Security', fields=['name as loan_security',
+		'loan_security_code', 'loan_security_name', 'haircut', 'loan_security_type',
+		'disabled'])
+
+	for security in loan_security_details:
+		security.update({
+			'latest_price': flt(loan_security_price_map.get(security.loan_security)),
+			'valid_upto': lsp_validity_map.get(security.loan_security)
+		})
+
+		security_detail_map.setdefault(security.loan_security, security)
+
+	return security_detail_map
+
+def get_applicant_wise_total_loan_security_qty(filters, loan_security_details):
+	current_pledges = {}
+	total_value_map = {}
+	applicant_type_map = {}
+	applicant_wise_unpledges = {}
+	conditions = ""
+
+	if filters.get('company'):
+		conditions = "AND company = %(company)s"
+
+	unpledges = frappe.db.sql("""
+		SELECT up.applicant, u.loan_security, sum(u.qty) as qty
+		FROM `tabLoan Security Unpledge` up, `tabUnpledge` u
+		WHERE u.parent = up.name
+		AND up.status = 'Approved'
+		{conditions}
+		GROUP BY up.applicant, u.loan_security
+	""".format(conditions=conditions), filters, as_dict=1)
+
+	for unpledge in unpledges:
+		applicant_wise_unpledges.setdefault((unpledge.applicant, unpledge.loan_security), unpledge.qty)
+
+	pledges = frappe.db.sql("""
+		SELECT lp.applicant_type, lp.applicant, p.loan_security, sum(p.qty) as qty
+		FROM `tabLoan Security Pledge` lp, `tabPledge`p
+		WHERE p.parent = lp.name
+		AND lp.status = 'Pledged'
+		{conditions}
+		GROUP BY lp.applicant, p.loan_security
+	""".format(conditions=conditions), filters, as_dict=1)
+
+	for security in pledges:
+		current_pledges.setdefault((security.applicant, security.loan_security), security.qty)
+		total_value_map.setdefault(security.applicant, 0.0)
+		applicant_type_map.setdefault(security.applicant, security.applicant_type)
+
+		current_pledges[(security.applicant, security.loan_security)] -= \
+			applicant_wise_unpledges.get((security.applicant, security.loan_security), 0.0)
+
+		total_value_map[security.applicant] += current_pledges.get((security.applicant, security.loan_security)) \
+			* loan_security_details.get(security.loan_security, {}).get('latest_price', 0)
+
+	return current_pledges, total_value_map, applicant_type_map
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/loan_management/report/loan_interest_report/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/loan_management/report/loan_interest_report/__init__.py
diff --git a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.js b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.js
new file mode 100644
index 0000000..a227b6d
--- /dev/null
+++ b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.js
@@ -0,0 +1,16 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Loan Interest Report"] = {
+	"filters": [
+		{
+			"fieldname":"company",
+			"label": __("Company"),
+			"fieldtype": "Link",
+			"options": "Company",
+			"default": frappe.defaults.get_user_default("Company"),
+			"reqd": 1
+		}
+	]
+};
diff --git a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.json b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.json
new file mode 100644
index 0000000..321d606
--- /dev/null
+++ b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.json
@@ -0,0 +1,29 @@
+{
+ "add_total_row": 1,
+ "columns": [],
+ "creation": "2021-01-10 02:03:26.742693",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2021-01-10 02:03:26.742693",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Loan Interest Report",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Loan Interest Accrual",
+ "report_name": "Loan Interest Report",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "System Manager"
+  },
+  {
+   "role": "Loan Manager"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py
new file mode 100644
index 0000000..2bfe6d3
--- /dev/null
+++ b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py
@@ -0,0 +1,183 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+import erpnext
+from frappe import _
+from frappe.utils import flt, getdate, add_days
+from erpnext.loan_management.report.applicant_wise_loan_security_exposure.applicant_wise_loan_security_exposure \
+	 import get_loan_security_details
+
+
+def execute(filters=None):
+	columns = get_columns(filters)
+	data = get_active_loan_details(filters)
+	return columns, data
+
+def get_columns(filters):
+	columns = [
+		{"label": _("Loan"), "fieldname": "loan", "fieldtype": "Link", "options": "Loan", "width": 160},
+		{"label": _("Status"), "fieldname": "status", "fieldtype": "Data", "width": 160},
+		{"label": _("Applicant Type"), "fieldname": "applicant_type", "options": "DocType", "width": 100},
+		{"label": _("Applicant Name"), "fieldname": "applicant_name", "fieldtype": "Dynamic Link", "options": "applicant_type", "width": 150},
+		{"label": _("Loan Type"), "fieldname": "loan_type", "fieldtype": "Link", "options": "Loan Type", "width": 100},
+		{"label": _("Sanctioned Amount"), "fieldname": "sanctioned_amount", "fieldtype": "Currency", "options": "currency", "width": 120},
+		{"label": _("Disbursed Amount"), "fieldname": "disbursed_amount", "fieldtype": "Currency", "options": "currency", "width": 120},
+		{"label": _("Penalty Amount"), "fieldname": "penalty", "fieldtype": "Currency", "options": "currency", "width": 120},
+		{"label": _("Accrued Interest"), "fieldname": "accrued_interest", "fieldtype": "Currency", "options": "currency", "width": 120},
+		{"label": _("Total Repayment"), "fieldname": "total_repayment", "fieldtype": "Currency", "options": "currency", "width": 120},
+		{"label": _("Principal Outstanding"), "fieldname": "principal_outstanding", "fieldtype": "Currency", "options": "currency", "width": 120},
+		{"label": _("Interest Outstanding"), "fieldname": "interest_outstanding", "fieldtype": "Currency", "options": "currency", "width": 120},
+		{"label": _("Total Outstanding"), "fieldname": "total_outstanding", "fieldtype": "Currency", "options": "currency", "width": 120},
+		{"label": _("Undue Booked Interest"), "fieldname": "undue_interest", "fieldtype": "Currency", "options": "currency", "width": 120},
+		{"label": _("Interest %"), "fieldname": "rate_of_interest", "fieldtype": "Percent", "width": 100},
+		{"label": _("Penalty Interest %"), "fieldname": "penalty_interest", "fieldtype": "Percent", "width": 100},
+		{"label": _("Loan To Value Ratio"), "fieldname": "loan_to_value", "fieldtype": "Percent", "width": 100},
+		{"label": _("Currency"), "fieldname": "currency", "fieldtype": "Currency", "options": "Currency", "hidden": 1, "width": 100},
+	]
+
+	return columns
+
+def get_active_loan_details(filters):
+
+	filter_obj = {"status": ("!=", "Closed")}
+	if filters.get('company'):
+		filter_obj.update({'company': filters.get('company')})
+
+	loan_details = frappe.get_all("Loan",
+		fields=["name as loan", "applicant_type", "applicant as applicant_name", "loan_type",
+		"disbursed_amount", "rate_of_interest", "total_payment", "total_principal_paid",
+		"total_interest_payable", "written_off_amount", "status"],
+		filters=filter_obj)
+
+	loan_list = [d.loan for d in loan_details]
+
+	current_pledges = get_loan_wise_pledges(filters)
+	loan_wise_security_value = get_loan_wise_security_value(filters, current_pledges)
+
+	sanctioned_amount_map = get_sanctioned_amount_map()
+	penal_interest_rate_map = get_penal_interest_rate_map()
+	payments = get_payments(loan_list)
+	accrual_map = get_interest_accruals(loan_list)
+	currency = erpnext.get_company_currency(filters.get('company'))
+
+	for loan in loan_details:
+		loan.update({
+			"sanctioned_amount": flt(sanctioned_amount_map.get(loan.applicant_name)),
+			"principal_outstanding": flt(loan.total_payment) - flt(loan.total_principal_paid) \
+				- flt(loan.total_interest_payable) - flt(loan.written_off_amount),
+			"total_repayment": flt(payments.get(loan.loan)),
+			"accrued_interest": flt(accrual_map.get(loan.loan, {}).get("accrued_interest")),
+			"interest_outstanding": flt(accrual_map.get(loan.loan, {}).get("interest_outstanding")),
+			"penalty": flt(accrual_map.get(loan.loan, {}).get("penalty")),
+			"penalty_interest": penal_interest_rate_map.get(loan.loan_type),
+			"undue_interest": flt(accrual_map.get(loan.loan, {}).get("undue_interest")),
+			"loan_to_value": 0.0,
+			"currency": currency
+		})
+
+		loan['total_outstanding'] = loan['principal_outstanding'] + loan['interest_outstanding'] \
+			+ loan['penalty']
+
+		if loan_wise_security_value.get(loan.loan):
+			loan['loan_to_value'] = (loan['principal_outstanding'] * 100) / loan_wise_security_value.get(loan.loan)
+
+	return loan_details
+
+def get_sanctioned_amount_map():
+	return frappe._dict(frappe.get_all("Sanctioned Loan Amount", fields=["applicant", "sanctioned_amount_limit"],
+		as_list=1))
+
+def get_payments(loans):
+	return frappe._dict(frappe.get_all("Loan Repayment", fields=["against_loan", "sum(amount_paid)"],
+		filters={"against_loan": ("in", loans)}, group_by="against_loan", as_list=1))
+
+def get_interest_accruals(loans):
+	accrual_map = {}
+
+	interest_accruals = frappe.get_all("Loan Interest Accrual",
+		fields=["loan", "interest_amount", "posting_date", "penalty_amount",
+		"paid_interest_amount", "accrual_type"], filters={"loan": ("in", loans)}, order_by="posting_date desc")
+
+	for entry in interest_accruals:
+		accrual_map.setdefault(entry.loan, {
+			"accrued_interest": 0.0,
+			"undue_interest": 0.0,
+			"interest_outstanding": 0.0,
+			"last_accrual_date": '',
+			"due_date": ''
+		})
+
+		if entry.accrual_type == 'Regular':
+			if not accrual_map[entry.loan]['due_date']:
+				accrual_map[entry.loan]['due_date'] = add_days(entry.posting_date, 1)
+			if not accrual_map[entry.loan]['last_accrual_date']:
+				accrual_map[entry.loan]['last_accrual_date'] = entry.posting_date
+
+		due_date = accrual_map[entry.loan]['due_date']
+		last_accrual_date = accrual_map[entry.loan]['last_accrual_date']
+
+		if due_date and getdate(entry.posting_date) < getdate(due_date):
+			accrual_map[entry.loan]["interest_outstanding"] += entry.interest_amount - entry.paid_interest_amount
+		else:
+			accrual_map[entry.loan]['undue_interest'] += entry.interest_amount - entry.paid_interest_amount
+
+		accrual_map[entry.loan]["accrued_interest"] += entry.interest_amount
+
+		if last_accrual_date and getdate(entry.posting_date) == last_accrual_date:
+			accrual_map[entry.loan]["penalty"] = entry.penalty_amount
+
+	return accrual_map
+
+def get_penal_interest_rate_map():
+	return frappe._dict(frappe.get_all("Loan Type", fields=["name", "penalty_interest_rate"], as_list=1))
+
+def get_loan_wise_pledges(filters):
+	loan_wise_unpledges = {}
+	current_pledges = {}
+
+	conditions = ""
+
+	if filters.get('company'):
+		conditions = "AND company = %(company)s"
+
+	unpledges = frappe.db.sql("""
+		SELECT up.loan, u.loan_security, sum(u.qty) as qty
+		FROM `tabLoan Security Unpledge` up, `tabUnpledge` u
+		WHERE u.parent = up.name
+		AND up.status = 'Approved'
+		{conditions}
+		GROUP BY up.loan
+	""".format(conditions=conditions), filters, as_dict=1)
+
+	for unpledge in unpledges:
+		loan_wise_unpledges.setdefault((unpledge.loan, unpledge.loan_security), unpledge.qty)
+
+	pledges = frappe.db.sql("""
+		SELECT lp.loan, p.loan_security, sum(p.qty) as qty
+		FROM `tabLoan Security Pledge` lp, `tabPledge`p
+		WHERE p.parent = lp.name
+		AND lp.status = 'Pledged'
+		{conditions}
+		GROUP BY lp.loan
+	""".format(conditions=conditions), filters, as_dict=1)
+
+	for security in pledges:
+		current_pledges.setdefault((security.loan, security.loan_security), security.qty)
+		current_pledges[(security.loan, security.loan_security)] -= \
+			loan_wise_unpledges.get((security.loan, security.loan_security), 0.0)
+
+	return current_pledges
+
+def get_loan_wise_security_value(filters, current_pledges):
+	loan_security_details = get_loan_security_details(filters)
+	loan_wise_security_value = {}
+
+	for key in current_pledges:
+		qty = current_pledges.get(key)
+		loan_wise_security_value.setdefault(key[0], 0.0)
+		loan_wise_security_value[key[0]] += \
+			flt(qty * loan_security_details.get(key[1], {}).get('latest_price', 0))
+
+	return loan_wise_security_value
\ No newline at end of file
diff --git a/erpnext/loan_management/report/loan_repayment_and_closure/loan_repayment_and_closure.py b/erpnext/loan_management/report/loan_repayment_and_closure/loan_repayment_and_closure.py
index b63cc8e..c6f6b99 100644
--- a/erpnext/loan_management/report/loan_repayment_and_closure/loan_repayment_and_closure.py
+++ b/erpnext/loan_management/report/loan_repayment_and_closure/loan_repayment_and_closure.py
@@ -103,7 +103,7 @@
 
 	loan_repayments = frappe.get_all("Loan Repayment",
 		filters = query_filters,
-		fields=["posting_date", "applicant", "name", "against_loan", "payment_type", "payable_amount",
+		fields=["posting_date", "applicant", "name", "against_loan", "payable_amount",
 			"pending_principal_amount", "interest_payable", "penalty_amount", "amount_paid"]
 	)
 
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/loan_management/report/loan_security_exposure/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/loan_management/report/loan_security_exposure/__init__.py
diff --git a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.js b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.js
new file mode 100644
index 0000000..777f296
--- /dev/null
+++ b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.js
@@ -0,0 +1,16 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Loan Security Exposure"] = {
+	"filters": [
+		{
+			"fieldname":"company",
+			"label": __("Company"),
+			"fieldtype": "Link",
+			"options": "Company",
+			"default": frappe.defaults.get_user_default("Company"),
+			"reqd": 1
+		}
+	]
+};
diff --git a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.json b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.json
new file mode 100644
index 0000000..d4dca08
--- /dev/null
+++ b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.json
@@ -0,0 +1,29 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-01-16 08:08:01.694583",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2021-01-16 08:08:01.694583",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Loan Security Exposure",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Loan Security",
+ "report_name": "Loan Security Exposure",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "System Manager"
+  },
+  {
+   "role": "Loan Manager"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py
new file mode 100644
index 0000000..adc8013
--- /dev/null
+++ b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py
@@ -0,0 +1,84 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import erpnext
+from frappe import _
+from frappe.utils import flt
+from six import iteritems
+from erpnext.loan_management.report.applicant_wise_loan_security_exposure.applicant_wise_loan_security_exposure \
+	 import get_loan_security_details, get_applicant_wise_total_loan_security_qty
+
+def execute(filters=None):
+	columns = get_columns(filters)
+	data = get_data(filters)
+	return columns, data
+
+def get_columns(filters):
+	columns = [
+		{"label": _("Loan Security"), "fieldname": "loan_security", "fieldtype": "Link", "options": "Loan Security", "width": 160},
+		{"label": _("Loan Security Code"), "fieldname": "loan_security_code", "fieldtype": "Data", "width": 100},
+		{"label": _("Loan Security Name"), "fieldname": "loan_security_name", "fieldtype": "Data", "width": 150},
+		{"label": _("Haircut"), "fieldname": "haircut", "fieldtype": "Percent", "width": 100},
+		{"label": _("Loan Security Type"), "fieldname": "loan_security_type", "fieldtype": "Link", "options": "Loan Security Type", "width": 120},
+		{"label": _("Disabled"), "fieldname": "disabled", "fieldtype": "Check", "width": 80},
+		{"label": _("Total Qty"), "fieldname": "total_qty", "fieldtype": "Float", "width": 100},
+		{"label": _("Latest Price"), "fieldname": "latest_price", "fieldtype": "Currency", "options": "currency", "width": 100},
+		{"label": _("Price Valid Upto"), "fieldname": "price_valid_upto", "fieldtype": "Datetime", "width": 100},
+		{"label": _("Current Value"), "fieldname": "current_value", "fieldtype": "Currency", "options": "currency", "width": 100},
+		{"label": _("% Of Total Portfolio"), "fieldname": "portfolio_percent", "fieldtype": "Percentage", "width": 100},
+		{"label": _("Pledged Applicant Count"), "fieldname": "pledged_applicant_count", "fieldtype": "Percentage", "width": 100},
+		{"label": _("Currency"), "fieldname": "currency", "fieldtype": "Currency", "options": "Currency", "hidden": 1, "width": 100},
+	]
+
+	return columns
+
+def get_data(filters):
+	data = []
+	loan_security_details = get_loan_security_details(filters)
+	current_pledges, total_portfolio_value = get_company_wise_loan_security_details(filters, loan_security_details)
+	currency = erpnext.get_company_currency(filters.get('company'))
+
+	for security, value in iteritems(current_pledges):
+		if value.get('qty'):
+			row = {}
+			current_value = flt(value.get('qty', 0) * loan_security_details.get(security, {}).get('latest_price', 0))
+			valid_upto = loan_security_details.get(security, {}).get('valid_upto')
+
+			row.update(loan_security_details.get(security))
+			row.update({
+				'total_qty': value.get('qty'),
+				'current_value': current_value,
+				'price_valid_upto': valid_upto,
+				'portfolio_percent': flt(current_value * 100 / total_portfolio_value, 2),
+				'pledged_applicant_count': value.get('applicant_count'),
+				'currency': currency
+			})
+
+			data.append(row)
+
+	return data
+
+
+def get_company_wise_loan_security_details(filters, loan_security_details):
+	pledge_values, total_value_map, applicant_type_map = get_applicant_wise_total_loan_security_qty(filters,
+		loan_security_details)
+
+	total_portfolio_value = 0
+	security_wise_map = {}
+	for key, qty in iteritems(pledge_values):
+		security_wise_map.setdefault(key[1], {
+			'qty': 0.0,
+			'applicant_count': 0.0
+		})
+
+		security_wise_map[key[1]]['qty'] += qty
+		if qty:
+			security_wise_map[key[1]]['applicant_count'] += 1
+
+		total_portfolio_value += flt(qty * loan_security_details.get(key[1])['latest_price'])
+
+	return security_wise_map, total_portfolio_value
+
+
+
diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js
index 1c4b7a1..15affd8 100644
--- a/erpnext/manufacturing/doctype/bom/bom.js
+++ b/erpnext/manufacturing/doctype/bom/bom.js
@@ -411,7 +411,7 @@
 
 cur_frm.cscript.time_in_mins = cur_frm.cscript.hour_rate;
 
-cur_frm.cscript.bom_no	= function(doc, cdt, cdn) {
+cur_frm.cscript.bom_no = function(doc, cdt, cdn) {
 	get_bom_material_detail(doc, cdt, cdn, false);
 };
 
@@ -419,17 +419,22 @@
 	if (doc.is_default) cur_frm.set_value("is_active", 1);
 };
 
-var get_bom_material_detail= function(doc, cdt, cdn, scrap_items) {
+var get_bom_material_detail = function(doc, cdt, cdn, scrap_items) {
+	if (!doc.company) {
+		frappe.throw({message: __("Please select a Company first."), title: __("Mandatory")});
+	}
+
 	var d = locals[cdt][cdn];
 	if (d.item_code) {
 		return frappe.call({
 			doc: doc,
 			method: "get_bom_material_detail",
 			args: {
-				'item_code': d.item_code,
-				'bom_no': d.bom_no != null ? d.bom_no: '',
+				"company": doc.company,
+				"item_code": d.item_code,
+				"bom_no": d.bom_no != null ? d.bom_no: '',
 				"scrap_items": scrap_items,
-				'qty': d.qty,
+				"qty": d.qty,
 				"stock_qty": d.stock_qty,
 				"include_item_in_manufacturing": d.include_item_in_manufacturing,
 				"uom": d.uom,
@@ -468,7 +473,7 @@
 	}
 
 	if (d.bom_no) {
-		frappe.msgprint(__("You can not change rate if BOM mentioned agianst any item"));
+		frappe.msgprint(__("You cannot change the rate if BOM is mentioned against any Item."));
 		get_bom_material_detail(doc, cdt, cdn, scrap_items);
 	} else {
 		erpnext.bom.calculate_rm_cost(doc);
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 6363242..03beedb 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -65,6 +65,10 @@
 
 	def validate(self):
 		self.route = frappe.scrub(self.name).replace('_', '-')
+
+		if not self.company:
+			frappe.throw(_("Please select a Company first."), title=_("Mandatory"))
+
 		self.clear_operations()
 		self.validate_main_item()
 		self.validate_currency()
@@ -125,6 +129,7 @@
 			self.validate_bom_currecny(item)
 
 			ret = self.get_bom_material_detail({
+				"company": self.company,
 				"item_code": item.item_code,
 				"item_name": item.item_name,
 				"bom_no": item.bom_no,
@@ -213,6 +218,7 @@
 
 		for d in self.get("items"):
 			rate = self.get_rm_rate({
+				"company": self.company,
 				"item_code": d.item_code,
 				"bom_no": d.bom_no,
 				"qty": d.qty,
@@ -611,10 +617,20 @@
 	""" Get weighted average of valuation rate from all warehouses """
 
 	total_qty, total_value, valuation_rate = 0.0, 0.0, 0.0
-	for d in frappe.db.sql("""select actual_qty, stock_value from `tabBin`
-		where item_code=%s""", args['item_code'], as_dict=1):
-			total_qty += flt(d.actual_qty)
-			total_value += flt(d.stock_value)
+	item_bins = frappe.db.sql("""
+		select
+			bin.actual_qty, bin.stock_value
+		from
+			`tabBin` bin, `tabWarehouse` warehouse
+		where
+			bin.item_code=%(item)s
+			and bin.warehouse = warehouse.name
+			and warehouse.company=%(company)s""",
+		{"item": args['item_code'], "company": args['company']}, as_dict=1)
+
+	for d in item_bins:
+		total_qty += flt(d.actual_qty)
+		total_value += flt(d.stock_value)
 
 	if total_qty:
 		valuation_rate =  total_value / total_qty
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index d15d81e..662a06b 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -17,6 +17,7 @@
 
 class OperationMismatchError(frappe.ValidationError): pass
 class OperationSequenceError(frappe.ValidationError): pass
+class JobCardCancelError(frappe.ValidationError): pass
 
 class JobCard(Document):
 	def validate(self):
@@ -217,39 +218,66 @@
 		field = "operation_id"
 		data = self.get_current_operation_data()
 		if data and len(data) > 0:
-			for_quantity = data[0].completed_qty
-			time_in_mins = data[0].time_in_mins
+			for_quantity = flt(data[0].completed_qty)
+			time_in_mins = flt(data[0].time_in_mins)
 
-		if self.get(field):
-			time_data = frappe.db.sql("""
+		wo = frappe.get_doc('Work Order', self.work_order)
+		if self.operation_id:
+			self.validate_produced_quantity(for_quantity, wo)
+			self.update_work_order_data(for_quantity, time_in_mins, wo)
+
+	def validate_produced_quantity(self, for_quantity, wo):
+		if self.docstatus < 2: return
+
+		if wo.produced_qty > for_quantity:
+			first_part_msg = (_("The {0} {1} is used to calculate the valuation cost for the finished good {2}.")
+				.format(frappe.bold(_("Job Card")), frappe.bold(self.name), frappe.bold(self.production_item)))
+
+			second_part_msg = (_("Kindly cancel the Manufacturing Entries first against the work order {0}.")
+				.format(frappe.bold(get_link_to_form("Work Order", self.work_order))))
+
+			frappe.throw(_("{0} {1}").format(first_part_msg, second_part_msg),
+				JobCardCancelError, title = _("Error"))
+
+	def update_work_order_data(self, for_quantity, time_in_mins, wo):
+		time_data = frappe.db.sql("""
 				SELECT
 					min(from_time) as start_time, max(to_time) as end_time
 				FROM `tabJob Card` jc, `tabJob Card Time Log` jctl
 				WHERE
 					jctl.parent = jc.name and jc.work_order = %s
-					and jc.{0} = %s and jc.docstatus = 1
-			""".format(field), (self.work_order, self.get(field)), as_dict=1)
+					and jc.operation_id = %s and jc.docstatus = 1
+			""", (self.work_order, self.operation_id), as_dict=1)
 
-			wo = frappe.get_doc('Work Order', self.work_order)
+		for data in wo.operations:
+			if data.get("name") == self.operation_id:
+				data.completed_qty = for_quantity
+				data.actual_operation_time = time_in_mins
+				data.actual_start_time = time_data[0].start_time if time_data else None
+				data.actual_end_time = time_data[0].end_time if time_data else None
 
-			for data in wo.operations:
-				if data.get("name") == self.get(field):
-					data.completed_qty = for_quantity
-					data.actual_operation_time = time_in_mins
-					data.actual_start_time = time_data[0].start_time if time_data else None
-					data.actual_end_time = time_data[0].end_time if time_data else None
-
-			wo.flags.ignore_validate_update_after_submit = True
-			wo.update_operation_status()
-			wo.calculate_operating_cost()
-			wo.set_actual_dates()
-			wo.save()
+		wo.flags.ignore_validate_update_after_submit = True
+		wo.update_operation_status()
+		wo.calculate_operating_cost()
+		wo.set_actual_dates()
+		wo.save()
 
 	def get_current_operation_data(self):
 		return frappe.get_all('Job Card',
 			fields = ["sum(total_time_in_mins) as time_in_mins", "sum(total_completed_qty) as completed_qty"],
 			filters = {"docstatus": 1, "work_order": self.work_order, "operation_id": self.operation_id})
 
+	def set_transferred_qty_in_job_card(self, ste_doc):
+		for row in ste_doc.items:
+			if not row.job_card_item: continue
+
+			qty = frappe.db.sql(""" SELECT SUM(qty) from `tabStock Entry Detail` sed, `tabStock Entry` se
+				WHERE  sed.job_card_item = %s and se.docstatus = 1 and sed.parent = se.name and
+				se.purpose = 'Material Transfer for Manufacture'
+			""", (row.job_card_item))[0][0]
+
+			frappe.db.set_value('Job Card Item', row.job_card_item, 'transferred_qty', flt(qty))
+
 	def set_transferred_qty(self, update_status=False):
 		if not self.items:
 			self.transferred_qty = self.for_quantity if self.docstatus == 1 else 0
@@ -262,7 +290,8 @@
 			self.transferred_qty = frappe.db.get_value('Stock Entry', {
 				'job_card': self.name,
 				'work_order': self.work_order,
-				'docstatus': 1
+				'docstatus': 1,
+				'purpose': 'Material Transfer for Manufacture'
 			}, 'sum(fg_completed_qty)') or 0
 
 		self.db_set("transferred_qty", self.transferred_qty)
@@ -403,6 +432,7 @@
 		target.purpose = "Material Transfer for Manufacture"
 		target.from_bom = 1
 		target.fg_completed_qty = source.get('for_quantity', 0) - source.get('transferred_qty', 0)
+		target.set_transfer_qty()
 		target.calculate_rate_and_amount()
 		target.set_missing_values()
 		target.set_stock_entry_type()
@@ -420,9 +450,10 @@
 			"field_map": {
 				"source_warehouse": "s_warehouse",
 				"required_qty": "qty",
-				"uom": "stock_uom"
+				"name": "job_card_item"
 			},
 			"postprocess": update_item,
+			"condition": lambda doc: doc.required_qty > 0
 		}
 	}, target_doc, set_missing_values)
 
diff --git a/erpnext/manufacturing/doctype/job_card_item/job_card_item.json b/erpnext/manufacturing/doctype/job_card_item/job_card_item.json
index bc9fe10..100ef4c 100644
--- a/erpnext/manufacturing/doctype/job_card_item/job_card_item.json
+++ b/erpnext/manufacturing/doctype/job_card_item/job_card_item.json
@@ -1,363 +1,120 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2018-07-09 17:20:44.737289", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "actions": [],
+ "creation": "2018-07-09 17:20:44.737289",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "item_code",
+  "source_warehouse",
+  "uom",
+  "item_group",
+  "column_break_3",
+  "stock_uom",
+  "item_name",
+  "description",
+  "qty_section",
+  "required_qty",
+  "column_break_9",
+  "transferred_qty",
+  "allow_alternative_item"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "item_code", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Item Code", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Item", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Item Code",
+   "options": "Item",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "source_warehouse", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 1, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Source Warehouse", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Warehouse", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "source_warehouse",
+   "fieldtype": "Link",
+   "ignore_user_permissions": 1,
+   "in_list_view": 1,
+   "label": "Source Warehouse",
+   "options": "Warehouse"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "uom", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "UOM", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "UOM", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "uom",
+   "fieldtype": "Link",
+   "label": "UOM",
+   "options": "UOM"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_3", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "item_name", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Item Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "item_name",
+   "fieldtype": "Data",
+   "label": "Item Name",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "description", 
-   "fieldtype": "Text", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Description", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "description",
+   "fieldtype": "Text",
+   "label": "Description",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "qty_section", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Qty", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "qty_section",
+   "fieldtype": "Section Break",
+   "label": "Qty"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "required_qty", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Required Qty", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "required_qty",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Required Qty",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_9", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_9",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "allow_alternative_item", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Allow Alternative Item", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
+   "default": "0",
+   "fieldname": "allow_alternative_item",
+   "fieldtype": "Check",
+   "label": "Allow Alternative Item"
+  },
+  {
+   "fetch_from": "item_code.item_group",
+   "fieldname": "item_group",
+   "fieldtype": "Link",
+   "label": "Item Group",
+   "options": "Item Group",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "item_code.stock_uom",
+   "fieldname": "stock_uom",
+   "fieldtype": "Link",
+   "label": "Stock UOM",
+   "options": "UOM"
+  },
+  {
+   "fieldname": "transferred_qty",
+   "fieldtype": "Float",
+   "label": "Transferred Qty",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2018-08-28 15:23:48.099459", 
- "modified_by": "Administrator", 
- "module": "Manufacturing", 
- "name": "Job Card Item", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0, 
- "track_views": 0
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-02-11 13:50:13.804108",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Job Card Item",
+ "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/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index ce9699e..00e8c54 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -5,7 +5,7 @@
 from __future__ import unicode_literals
 import unittest
 import frappe
-from frappe.utils import flt, time_diff_in_hours, now, add_months, cint, today
+from frappe.utils import flt, now, add_months, cint, today, add_to_date
 from erpnext.manufacturing.doctype.work_order.work_order import (make_stock_entry,
 	ItemHasVariantError, stop_unstop, StockOverProductionError, OverProductionError, CapacityError)
 from erpnext.stock.doctype.stock_entry import test_stock_entry
@@ -14,6 +14,7 @@
 from erpnext.stock.doctype.item.test_item import make_item
 from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+from erpnext.manufacturing.doctype.job_card.job_card import JobCardCancelError
 
 class TestWorkOrder(unittest.TestCase):
 	def setUp(self):
@@ -93,11 +94,11 @@
 		wo_order = make_wo_order_test_record(item="_Test FG Item", qty=2,
 			source_warehouse=warehouse, skip_transfer=1)
 
-		bin1_on_submit = get_bin(item, warehouse)
+		reserved_qty_on_submission = cint(get_bin(item, warehouse).reserved_qty_for_production)
 
 		# reserved qty for production is updated
-		self.assertEqual(cint(bin1_at_start.reserved_qty_for_production) + 2,
-			cint(bin1_on_submit.reserved_qty_for_production))
+		self.assertEqual(cint(bin1_at_start.reserved_qty_for_production) + 2, reserved_qty_on_submission)
+
 
 		test_stock_entry.make_stock_entry(item_code="_Test Item",
 			target=warehouse, qty=100, basic_rate=100)
@@ -108,9 +109,9 @@
 		s.submit()
 
 		bin1_at_completion = get_bin(item, warehouse)
-
+		
 		self.assertEqual(cint(bin1_at_completion.reserved_qty_for_production),
-			cint(bin1_on_submit.reserved_qty_for_production) - 1)
+			reserved_qty_on_submission - 1)
 
 	def test_production_item(self):
 		wo_order = make_wo_order_test_record(item="_Test FG Item", qty=1, do_not_save=True)
@@ -369,21 +370,49 @@
 		self.assertEqual(ste.total_additional_costs, 1000)
 
 	def test_job_card(self):
+		stock_entries = []
 		data = frappe.get_cached_value('BOM',
 			{'docstatus': 1, 'with_operations': 1, 'company': '_Test Company'}, ['name', 'item'])
 
-		if data:
-			frappe.db.set_value("Manufacturing Settings",
-				None, "disable_capacity_planning", 0)
+		bom, bom_item = data
 
-			bom, bom_item = data
+		bom_doc = frappe.get_doc('BOM', bom)
+		work_order = make_wo_order_test_record(item=bom_item, qty=1,
+			bom_no=bom, source_warehouse="_Test Warehouse - _TC")
 
-			bom_doc = frappe.get_doc('BOM', bom)
-			work_order = make_wo_order_test_record(item=bom_item, qty=1, bom_no=bom)
-			self.assertTrue(work_order.planned_end_date)
+		for row in work_order.required_items:
+			stock_entry_doc = test_stock_entry.make_stock_entry(item_code=row.item_code,
+				target="_Test Warehouse - _TC", qty=row.required_qty, basic_rate=100)
+			stock_entries.append(stock_entry_doc)
 
-			job_cards = frappe.get_all('Job Card', filters = {'work_order': work_order.name})
-			self.assertEqual(len(job_cards), len(bom_doc.operations))
+		ste = frappe.get_doc(make_stock_entry(work_order.name, "Material Transfer for Manufacture", 1))
+		ste.submit()
+		stock_entries.append(ste)
+
+		job_cards = frappe.get_all('Job Card', filters = {'work_order': work_order.name})
+		self.assertEqual(len(job_cards), len(bom_doc.operations))
+
+		for i, job_card in enumerate(job_cards):
+			doc = frappe.get_doc("Job Card", job_card)
+			doc.append("time_logs", {
+				"from_time": now(),
+				"hours": i,
+				"to_time": add_to_date(now(), i),
+				"completed_qty": doc.for_quantity
+			})
+			doc.submit()
+
+		ste1 = frappe.get_doc(make_stock_entry(work_order.name, "Manufacture", 1))
+		ste1.submit()
+		stock_entries.append(ste1)
+
+		for job_card in job_cards:
+			doc = frappe.get_doc("Job Card", job_card)
+			self.assertRaises(JobCardCancelError, doc.cancel)
+
+		stock_entries.reverse()
+		for stock_entry in stock_entries:
+			stock_entry.cancel()
 
 	def test_capcity_planning(self):
 		frappe.db.set_value("Manufacturing Settings", None, {
@@ -509,19 +538,60 @@
 		ste1.submit()
 		ste_cancel_list.append(ste1)
 
-		print(wo_order.name)
 		ste3 = frappe.get_doc(make_stock_entry(wo_order.name, "Material Consumption for Manufacture", 2))
 		self.assertEquals(ste3.fg_completed_qty, 2)
 
 		expected_qty = {"_Test Item": 2, "_Test Item Home Desktop 100": 4}
 		for row in ste3.items:
 			self.assertEquals(row.qty, expected_qty.get(row.item_code))
-
+		ste_cancel_list.reverse()
 		for ste_doc in ste_cancel_list:
 			ste_doc.cancel()
 
 		frappe.db.set_value("Manufacturing Settings", None, "material_consumption", 0)
 
+	def test_extra_material_transfer(self):
+		frappe.db.set_value("Manufacturing Settings", None, "material_consumption", 0)
+		frappe.db.set_value("Manufacturing Settings", None, "backflush_raw_materials_based_on",
+			"Material Transferred for Manufacture")
+
+		wo_order = make_wo_order_test_record(planned_start_date=now(), qty=4)
+
+		ste_cancel_list = []
+		ste1 = test_stock_entry.make_stock_entry(item_code="_Test Item",
+			target="_Test Warehouse - _TC", qty=20, basic_rate=5000.0)
+		ste2 = test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
+			target="_Test Warehouse - _TC", qty=20, basic_rate=1000.0)
+
+		ste_cancel_list.extend([ste1, ste2])
+
+		itemwise_qty = {}
+		s = frappe.get_doc(make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 4))
+		for row in s.items:
+			row.qty = row.qty + 2
+			itemwise_qty.setdefault(row.item_code, row.qty)
+
+		s.submit()
+		ste_cancel_list.append(s)
+
+		ste3 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 2))
+		for ste_row in ste3.items:
+			if itemwise_qty.get(ste_row.item_code) and ste_row.s_warehouse:
+				self.assertEquals(ste_row.qty, itemwise_qty.get(ste_row.item_code) / 2)
+
+		ste3.submit()
+		ste_cancel_list.append(ste3)
+
+		ste2 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 2))
+		for ste_row in ste2.items:
+			if itemwise_qty.get(ste_row.item_code) and ste_row.s_warehouse:
+				self.assertEquals(ste_row.qty, itemwise_qty.get(ste_row.item_code) / 2)
+		ste_cancel_list.reverse()
+		for ste_doc in ste_cancel_list:
+			ste_doc.cancel()
+
+		frappe.db.set_value("Manufacturing Settings", None, "backflush_raw_materials_based_on", "BOM")
+
 def get_scrap_item_details(bom_no):
 	scrap_items = {}
 	for item in frappe.db.sql("""select item_code, stock_qty from `tabBOM Scrap Item`
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index cc93bf9..ca530bb 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -456,10 +456,10 @@
 
 			if data and len(data):
 				dates = [d.posting_datetime for d in data]
-				self.actual_start_date = min(dates)
+				self.db_set('actual_start_date', min(dates))
 
 				if self.status == "Completed":
-					self.actual_end_date = max(dates)
+					self.db_set('actual_end_date', max(dates))
 
 		self.set_lead_time()
 
@@ -725,6 +725,7 @@
 		args.update(item_data)
 
 		args["rate"] = get_bom_item_rate({
+			"company": wo_doc.company,
 			"item_code": args.get("item_code"),
 			"qty": args.get("required_qty"),
 			"uom": args.get("stock_uom"),
diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
index 75ebcbc..1c6758e 100644
--- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
+++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
@@ -20,6 +20,7 @@
 		_("Item") + ":Link/Item:150",
 		_("Description") + "::300",
 		_("BOM Qty") + ":Float:160",
+		_("BOM UoM") + "::160",
 		_("Required Qty") + ":Float:120",
 		_("In Stock Qty") + ":Float:120",
 		_("Enough Parts to Build") + ":Float:200",
@@ -32,7 +33,7 @@
 	bom = filters.get("bom")
 
 	table = "`tabBOM Item`"
-	qty_field = "qty"
+	qty_field = "stock_qty"
 
 	qty_to_produce = filters.get("qty_to_produce", 1)
 	if  int(qty_to_produce) <= 0:
@@ -40,7 +41,6 @@
 
 	if filters.get("show_exploded_view"):
 		table = "`tabBOM Explosion Item`"
-		qty_field = "stock_qty"
 
 	if filters.get("warehouse"):
 		warehouse_details = frappe.db.get_value("Warehouse", filters.get("warehouse"), ["lft", "rgt"], as_dict=1)
@@ -59,6 +59,7 @@
 				bom_item.item_code,
 				bom_item.description ,
 				bom_item.{qty_field},
+				bom_item.stock_uom,
 				bom_item.{qty_field} * {qty_to_produce} / bom.quantity,
 				sum(ledger.actual_qty) as actual_qty,
 				sum(FLOOR(ledger.actual_qty / (bom_item.{qty_field} * {qty_to_produce} / bom.quantity)))
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/non_profit/doctype/donation/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/non_profit/doctype/donation/__init__.py
diff --git a/erpnext/non_profit/doctype/donation/donation.js b/erpnext/non_profit/doctype/donation/donation.js
new file mode 100644
index 0000000..10e8220
--- /dev/null
+++ b/erpnext/non_profit/doctype/donation/donation.js
@@ -0,0 +1,26 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Donation', {
+	refresh: function(frm) {
+		if (frm.doc.docstatus === 1 && !frm.doc.paid) {
+			frm.add_custom_button(__('Create Payment Entry'), function() {
+				frm.events.make_payment_entry(frm);
+			});
+		}
+	},
+
+	make_payment_entry: function(frm) {
+		return frappe.call({
+			method: 'erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry',
+			args: {
+				'dt': frm.doc.doctype,
+				'dn': frm.doc.name
+			},
+			callback: function(r) {
+				var doc = frappe.model.sync(r.message);
+				frappe.set_route('Form', doc[0].doctype, doc[0].name);
+			}
+		});
+	},
+});
diff --git a/erpnext/non_profit/doctype/donation/donation.json b/erpnext/non_profit/doctype/donation/donation.json
new file mode 100644
index 0000000..6759569
--- /dev/null
+++ b/erpnext/non_profit/doctype/donation/donation.json
@@ -0,0 +1,156 @@
+{
+ "actions": [],
+ "autoname": "naming_series:",
+ "creation": "2021-02-17 10:28:52.645731",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "naming_series",
+  "donor",
+  "donor_name",
+  "email",
+  "column_break_4",
+  "company",
+  "date",
+  "payment_details_section",
+  "paid",
+  "amount",
+  "mode_of_payment",
+  "razorpay_payment_id",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "fieldname": "donor",
+   "fieldtype": "Link",
+   "label": "Donor",
+   "options": "Donor",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "donor.donor_name",
+   "fieldname": "donor_name",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Donor Name",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "donor.email",
+   "fieldname": "email",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Email",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "date",
+   "fieldtype": "Date",
+   "label": "Date",
+   "reqd": 1
+  },
+  {
+   "fieldname": "payment_details_section",
+   "fieldtype": "Section Break",
+   "label": "Payment Details"
+  },
+  {
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "label": "Amount",
+   "reqd": 1
+  },
+  {
+   "fieldname": "mode_of_payment",
+   "fieldtype": "Link",
+   "label": "Mode of Payment",
+   "options": "Mode of Payment"
+  },
+  {
+   "fieldname": "razorpay_payment_id",
+   "fieldtype": "Data",
+   "label": "Razorpay Payment ID",
+   "read_only": 1
+  },
+  {
+   "fieldname": "naming_series",
+   "fieldtype": "Select",
+   "label": "Naming Series",
+   "options": "NPO-DTN-.YYYY.-"
+  },
+  {
+   "default": "0",
+   "fieldname": "paid",
+   "fieldtype": "Check",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Paid"
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Donation",
+   "print_hide": 1,
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2021-03-11 10:53:11.269005",
+ "modified_by": "Administrator",
+ "module": "Non Profit",
+ "name": "Donation",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "select": 1,
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Non Profit Manager",
+   "select": 1,
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  }
+ ],
+ "search_fields": "donor_name, email",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "donor_name",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/donation/donation.py b/erpnext/non_profit/doctype/donation/donation.py
new file mode 100644
index 0000000..e947588
--- /dev/null
+++ b/erpnext/non_profit/doctype/donation/donation.py
@@ -0,0 +1,215 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+import six
+import json
+from frappe.model.document import Document
+from frappe import _
+from frappe.utils import getdate, flt, get_link_to_form
+from frappe.email import sendmail_to_system_managers
+from erpnext.non_profit.doctype.membership.membership import verify_signature
+
+class Donation(Document):
+	def validate(self):
+		if not self.donor or not frappe.db.exists('Donor', self.donor):
+			# for web forms
+			user_type = frappe.db.get_value('User', frappe.session.user, 'user_type')
+			if user_type == 'Website User':
+				self.create_donor_for_website_user()
+			else:
+				frappe.throw(_('Please select a Member'))
+
+	def create_donor_for_website_user(self):
+		donor_name = frappe.get_value('Donor', dict(email=frappe.session.user))
+
+		if not donor_name:
+			user = frappe.get_doc('User', frappe.session.user)
+			donor = frappe.get_doc(dict(
+				doctype='Donor',
+				donor_type=self.get('donor_type'),
+				email=frappe.session.user,
+				member_name=user.get_fullname()
+			)).insert(ignore_permissions=True)
+			donor_name = donor.name
+
+		if self.get('__islocal'):
+			self.donor = donor_name
+
+	def on_payment_authorized(self, *args, **kwargs):
+		self.load_from_db()
+		self.create_payment_entry()
+
+	def create_payment_entry(self):
+		settings = frappe.get_doc('Non Profit Settings')
+		if not settings.automate_donation_payment_entries:
+			return
+
+		if not settings.donation_payment_account:
+			frappe.throw(_('You need to set <b>Payment Account</b> for Donation in {0}').format(
+				get_link_to_form('Non Profit Settings', 'Non Profit Settings')))
+
+		from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
+
+		frappe.flags.ignore_account_permission = True
+		pe = get_payment_entry(dt=self.doctype, dn=self.name)
+		frappe.flags.ignore_account_permission = False
+		pe.paid_from = settings.donation_debit_account
+		pe.paid_to = settings.donation_payment_account
+		pe.reference_no = self.name
+		pe.reference_date = getdate()
+		pe.flags.ignore_mandatory = True
+		pe.insert()
+		pe.submit()
+
+
+@frappe.whitelist(allow_guest=True)
+def capture_razorpay_donations(*args, **kwargs):
+	"""
+		Creates Donation from Razorpay Webhook Request Data on payment.captured event
+		Creates Donor from email if not found
+	"""
+	data = frappe.request.get_data(as_text=True)
+
+	try:
+		verify_signature(data, endpoint='Donation')
+	except Exception as e:
+		log = frappe.log_error(e, 'Donation Webhook Verification Error')
+		notify_failure(log)
+		return { 'status': 'Failed', 'reason': e }
+
+	if isinstance(data, six.string_types):
+		data = json.loads(data)
+	data = frappe._dict(data)
+
+	payment = data.payload.get('payment', {}).get('entity', {})
+	payment = frappe._dict(payment)
+
+	try:
+		if not data.event == 'payment.captured':
+			return
+
+		donor = get_donor(payment.email)
+		if not donor:
+			donor = create_donor(payment)
+
+		donation = create_donation(donor, payment)
+		donation.run_method('create_payment_entry')
+
+	except Exception as e:
+		message = '{0}\n\n{1}\n\n{2}: {3}'.format(e, frappe.get_traceback(), _('Payment ID'), payment.id)
+		log = frappe.log_error(message, _('Error creating donation entry for {0}').format(donor.name))
+		notify_failure(log)
+		return { 'status': 'Failed', 'reason': e }
+
+	return { 'status': 'Success' }
+
+
+def create_donation(donor, payment):
+	if not frappe.db.exists('Mode of Payment', payment.method):
+		create_mode_of_payment(payment.method)
+
+	company = get_company_for_donations()
+	donation = frappe.get_doc({
+		'doctype': 'Donation',
+		'company': company,
+		'donor': donor.name,
+		'donor_name': donor.donor_name,
+		'email': donor.email,
+		'date': getdate(),
+		'amount': flt(payment.amount),
+		'mode_of_payment': payment.method,
+		'razorpay_payment_id': payment.id
+	}).insert(ignore_mandatory=True)
+
+	donation.submit()
+	return donation
+
+
+def get_donor(email):
+	donors = frappe.get_all('Donor',
+		filters={'email': email},
+		order_by='creation desc')
+
+	try:
+		return frappe.get_doc('Donor', donors[0]['name'])
+	except Exception:
+		return None
+
+
+@frappe.whitelist()
+def create_donor(payment):
+	donor_details = frappe._dict(payment)
+	donor_type = frappe.db.get_single_value('Non Profit Settings', 'default_donor_type')
+
+	donor = frappe.new_doc('Donor')
+	donor.update({
+		'donor_name': donor_details.email,
+		'donor_type': donor_type,
+		'email': donor_details.email,
+		'contact': donor_details.contact
+	})
+
+	if donor_details.get('notes'):
+		donor = get_additional_notes(donor, donor_details)
+
+	donor.insert(ignore_mandatory=True)
+	return donor
+
+
+def get_company_for_donations():
+	company = frappe.db.get_single_value('Non Profit Settings', 'donation_company')
+	if not company:
+		from erpnext.healthcare.setup import get_company
+		company = get_company()
+	return company
+
+
+def get_additional_notes(donor, donor_details):
+	if type(donor_details.notes) == dict:
+		for k, v in donor_details.notes.items():
+			notes = '\n'.join('{}: {}'.format(k, v))
+
+			# extract donor name from notes
+			if 'name' in k.lower():
+				donor.update({
+					'donor_name': donor_details.notes.get(k)
+				})
+
+			# extract pan from notes
+			if 'pan' in k.lower():
+				donor.update({
+					'pan_number': donor_details.notes.get(k)
+				})
+
+		donor.add_comment('Comment', notes)
+
+	elif type(donor_details.notes) == str:
+		donor.add_comment('Comment', donor_details.notes)
+
+	return donor
+
+
+def create_mode_of_payment(method):
+	frappe.get_doc({
+		'doctype': 'Mode of Payment',
+		'mode_of_payment': method
+	}).insert(ignore_mandatory=True)
+
+
+def notify_failure(log):
+	try:
+		content = '''
+			Dear System Manager,
+			Razorpay webhook for creating donation failed due to some reason.
+			Please check the error log linked below
+			Error Log: {0}
+			Regards, Administrator
+		'''.format(get_link_to_form('Error Log', log.name))
+
+		sendmail_to_system_managers(_('[Important] [ERPNext] Razorpay donation webhook failed, please check.'), content)
+	except Exception:
+		pass
+
diff --git a/erpnext/non_profit/doctype/donation/donation_dashboard.py b/erpnext/non_profit/doctype/donation/donation_dashboard.py
new file mode 100644
index 0000000..7e25c8d
--- /dev/null
+++ b/erpnext/non_profit/doctype/donation/donation_dashboard.py
@@ -0,0 +1,16 @@
+from __future__ import unicode_literals
+from frappe import _
+
+def get_data():
+	return {
+		'fieldname': 'donation',
+		'non_standard_fieldnames': {
+			'Payment Entry': 'reference_name'
+		},
+		'transactions': [
+			{
+				'label': _('Payment'),
+				'items': ['Payment Entry']
+			}
+		]
+	}
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/donation/test_donation.py b/erpnext/non_profit/doctype/donation/test_donation.py
new file mode 100644
index 0000000..c6a534d
--- /dev/null
+++ b/erpnext/non_profit/doctype/donation/test_donation.py
@@ -0,0 +1,76 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+from erpnext.non_profit.doctype.donation.donation import create_donation
+
+class TestDonation(unittest.TestCase):
+	def setUp(self):
+		create_donor_type()
+		settings = frappe.get_doc('Non Profit Settings')
+		settings.company = '_Test Company'
+		settings.donation_company = '_Test Company'
+		settings.default_donor_type = '_Test Donor'
+		settings.automate_donation_payment_entries = 1
+		settings.donation_debit_account = 'Debtors - _TC'
+		settings.donation_payment_account =  'Cash - _TC'
+		settings.creation_user = 'Administrator'
+		settings.flags.ignore_permissions = True
+		settings.save()
+
+	def test_payment_entry_for_donations(self):
+		donor = create_donor()
+		create_mode_of_payment()
+		payment = frappe._dict({
+			'amount': 100,
+			'method': 'Debit Card',
+			'id': 'pay_MeXAmsgeKOhq7O'
+		})
+		donation = create_donation(donor, payment)
+
+		self.assertTrue(donation.name)
+
+		# Naive test to check if at all payment entry is generated
+		# This method is actually triggered from Payment Gateway
+		# In any case if details were missing, this would throw an error
+		donation.on_payment_authorized()
+		donation.reload()
+
+		self.assertEquals(donation.paid, 1)
+		self.assertTrue(frappe.db.exists('Payment Entry', {'reference_no': donation.name}))
+
+
+def create_donor_type():
+	if not frappe.db.exists('Donor Type', '_Test Donor'):
+		frappe.get_doc({
+			'doctype': 'Donor Type',
+			'donor_type': '_Test Donor'
+		}).insert()
+
+
+def create_donor():
+	donor = frappe.db.exists('Donor', 'donor@test.com')
+	if donor:
+		return frappe.get_doc('Donor', 'donor@test.com')
+	else:
+		return frappe.get_doc({
+			'doctype': 'Donor',
+			'donor_name': '_Test Donor',
+			'donor_type': '_Test Donor',
+			'email': 'donor@test.com'
+		}).insert()
+
+
+def create_mode_of_payment():
+	if not frappe.db.exists('Mode of Payment', 'Debit Card'):
+		frappe.get_doc({
+			'doctype': 'Mode of Payment',
+			'mode_of_payment': 'Debit Card',
+			'accounts': [{
+				'company': '_Test Company',
+				'default_account': 'Cash - _TC'
+			}]
+		}).insert()
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/donor/donor.json b/erpnext/non_profit/doctype/donor/donor.json
index 9639265..72f24ef 100644
--- a/erpnext/non_profit/doctype/donor/donor.json
+++ b/erpnext/non_profit/doctype/donor/donor.json
@@ -76,8 +76,13 @@
   }
  ],
  "image_field": "image",
- "links": [],
- "modified": "2020-09-16 23:46:04.083274",
+ "links": [
+  {
+   "link_doctype": "Donation",
+   "link_fieldname": "donor"
+  }
+ ],
+ "modified": "2021-02-17 16:36:33.470731",
  "modified_by": "Administrator",
  "module": "Non Profit",
  "name": "Donor",
diff --git a/erpnext/non_profit/doctype/donor/donor.py b/erpnext/non_profit/doctype/donor/donor.py
index 9121d0c..fb70e59 100644
--- a/erpnext/non_profit/doctype/donor/donor.py
+++ b/erpnext/non_profit/doctype/donor/donor.py
@@ -11,3 +11,8 @@
 		"""Load address and contacts in `__onload`"""
 		load_address_and_contact(self)
 
+	def validate(self):
+		from frappe.utils import validate_email_address
+		if self.email:
+			validate_email_address(self.email.strip(), True)
+
diff --git a/erpnext/non_profit/doctype/member/member.js b/erpnext/non_profit/doctype/member/member.js
index 199dcfc..6b8f1b1 100644
--- a/erpnext/non_profit/doctype/member/member.js
+++ b/erpnext/non_profit/doctype/member/member.js
@@ -3,7 +3,7 @@
 
 frappe.ui.form.on('Member', {
 	setup: function(frm) {
-		frappe.db.get_single_value("Membership Settings", "enable_razorpay").then(val => {
+		frappe.db.get_single_value('Non Profit Settings', 'enable_razorpay_for_memberships').then(val => {
 			if (val && (frm.doc.subscription_id || frm.doc.customer_id)) {
 				frm.set_df_property('razorpay_details_section', 'hidden', false);
 			}
diff --git a/erpnext/non_profit/doctype/member/member.json b/erpnext/non_profit/doctype/member/member.json
index 992ef16..f190cfa 100644
--- a/erpnext/non_profit/doctype/member/member.json
+++ b/erpnext/non_profit/doctype/member/member.json
@@ -12,7 +12,6 @@
   "membership_expiry_date",
   "column_break_5",
   "membership_type",
-  "email",
   "email_id",
   "image",
   "customer_section",
@@ -65,13 +64,6 @@
    "reqd": 1
   },
   {
-   "fieldname": "email",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "User",
-   "options": "User"
-  },
-  {
    "fieldname": "image",
    "fieldtype": "Attach Image",
    "hidden": 1,
@@ -178,7 +170,7 @@
  ],
  "image_field": "image",
  "links": [],
- "modified": "2020-09-16 23:44:13.596948",
+ "modified": "2020-11-09 12:12:10.174647",
  "modified_by": "Administrator",
  "module": "Non Profit",
  "name": "Member",
diff --git a/erpnext/non_profit/doctype/member/member.py b/erpnext/non_profit/doctype/member/member.py
index 44b975e..3ba2ee7 100644
--- a/erpnext/non_profit/doctype/member/member.py
+++ b/erpnext/non_profit/doctype/member/member.py
@@ -7,7 +7,7 @@
 from frappe import _
 from frappe.model.document import Document
 from frappe.contacts.address_and_contact import load_address_and_contact
-from frappe.utils import cint
+from frappe.utils import cint, get_link_to_form
 from frappe.integrations.utils import get_payment_gateway_controller
 from erpnext.non_profit.doctype.membership_type.membership_type import get_membership_type
 
@@ -18,8 +18,6 @@
 
 
 	def validate(self):
-		if self.email:
-			self.validate_email_type(self.email)
 		if self.email_id:
 			self.validate_email_type(self.email_id)
 
@@ -28,9 +26,10 @@
 		validate_email_address(email.strip(), True)
 
 	def setup_subscription(self):
-		membership_settings = frappe.get_doc("Membership Settings")
-		if not membership_settings.enable_razorpay:
-			frappe.throw("Please enable Razorpay to setup subscription")
+		non_profit_settings = frappe.get_doc('Non Profit Settings')
+		if not non_profit_settings.enable_razorpay_for_memberships:
+			frappe.throw('Please check Enable Razorpay for Memberships in {0} to setup subscription').format(
+				get_link_to_form('Non Profit Settings', 'Non Profit Settings'))
 
 		controller = get_payment_gateway_controller("Razorpay")
 		settings = controller.get_settings({})
@@ -42,7 +41,7 @@
 
 		subscription_details = {
 			"plan_id": plan_id,
-			"billing_frequency": cint(membership_settings.billing_frequency),
+			"billing_frequency": cint(non_profit_settings.billing_frequency),
 			"customer_notify": 1
 		}
 
@@ -57,14 +56,16 @@
 	def make_customer_and_link(self):
 		if self.customer:
 			frappe.msgprint(_("A customer is already linked to this Member"))
-		cust = create_customer(frappe._dict({
+
+		customer = create_customer(frappe._dict({
 			'fullname': self.member_name,
-			'email': self.email_id or self.user,
+			'email': self.email_id,
 			'phone': None
 		}))
 
-		self.customer = cust
+		self.customer = customer
 		self.save()
+		frappe.msgprint(_("Customer {0} has been created succesfully.").format(self.customer))
 
 
 def get_or_create_member(user_details):
@@ -177,4 +178,4 @@
 			mobile=mobile
 		))
 
-		return member.name
\ No newline at end of file
+		return member.name
diff --git a/erpnext/non_profit/doctype/membership/membership.js b/erpnext/non_profit/doctype/membership/membership.js
index ee8a8c0..3187204 100644
--- a/erpnext/non_profit/doctype/membership/membership.js
+++ b/erpnext/non_profit/doctype/membership/membership.js
@@ -3,21 +3,30 @@
 
 frappe.ui.form.on('Membership', {
 	setup: function(frm) {
-		frappe.db.get_single_value("Membership Settings", "enable_razorpay").then(val => {
-			if (val) frm.set_df_property('razorpay_details_section', 'hidden', false);
+		frappe.db.get_single_value("Non Profit Settings", "enable_razorpay_for_memberships").then(val => {
+			if (val) frm.set_df_property("razorpay_details_section", "hidden", false);
 		})
 	},
 
 	refresh: function(frm) {
+		if (frm.doc.__islocal)
+			return;
+
 		!frm.doc.invoice && frm.add_custom_button("Generate Invoice", () => {
-			frm.call("generate_invoice", {
-				save: true
-			}).then(() => {
-				frm.reload_doc();
+			frm.call({
+				doc: frm.doc,
+				method: "generate_invoice",
+				args: {save: true},
+				freeze: true,
+				freeze_message: __("Creating Membership Invoice"),
+				callback: function(r) {
+					if (r.invoice)
+						frm.reload_doc();
+				}
 			});
 		});
 
-		frappe.db.get_single_value("Membership Settings", "send_email").then(val => {
+		frappe.db.get_single_value("Non Profit Settings", "send_email").then(val => {
 			if (val) frm.add_custom_button("Send Acknowledgement", () => {
 				frm.call("send_acknowlement").then(() => {
 					frm.reload_doc();
@@ -27,6 +36,6 @@
 	},
 
 	onload: function(frm) {
-		frm.add_fetch('membership_type', 'amount', 'amount');
+		frm.add_fetch("membership_type", "amount", "amount");
 	}
 });
diff --git a/erpnext/non_profit/doctype/membership/membership.json b/erpnext/non_profit/doctype/membership/membership.json
index 7f21896..11d32f9 100644
--- a/erpnext/non_profit/doctype/membership/membership.json
+++ b/erpnext/non_profit/doctype/membership/membership.json
@@ -7,8 +7,10 @@
  "engine": "InnoDB",
  "field_order": [
   "member",
+  "member_name",
   "membership_type",
   "column_break_3",
+  "company",
   "membership_status",
   "membership_validity_section",
   "from_date",
@@ -46,6 +48,8 @@
   {
    "fieldname": "membership_status",
    "fieldtype": "Select",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
    "label": "Membership Status",
    "options": "New\nCurrent\nExpired\nPending\nCancelled"
   },
@@ -122,11 +126,25 @@
    "fieldtype": "Link",
    "label": "Invoice",
    "options": "Sales Invoice"
+  },
+  {
+   "fetch_from": "member.member_name",
+   "fieldname": "member_name",
+   "fieldtype": "Data",
+   "label": "Member Name",
+   "read_only": 1
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-09-19 14:28:11.532696",
+ "modified": "2021-02-19 14:33:44.925122",
  "modified_by": "Administrator",
  "module": "Non Profit",
  "name": "Membership",
@@ -158,7 +176,9 @@
   }
  ],
  "restrict_to_domain": "Non Profit",
+ "search_fields": "member, member_name",
  "sort_field": "modified",
  "sort_order": "DESC",
+ "title_field": "member_name",
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py
index 7d15aba..191281f 100644
--- a/erpnext/non_profit/doctype/membership/membership.py
+++ b/erpnext/non_profit/doctype/membership/membership.py
@@ -6,6 +6,7 @@
 import json
 import frappe
 import six
+import os
 from datetime import datetime
 from frappe.model.document import Document
 from frappe.email import sendmail_to_system_managers
@@ -14,33 +15,43 @@
 from frappe import _
 import erpnext
 
-
 class Membership(Document):
 	def validate(self):
 		if not self.member or not frappe.db.exists("Member", self.member):
-			member_name = frappe.get_value('Member', dict(email=frappe.session.user))
+			# for web forms
+			user_type = frappe.db.get_value("User", frappe.session.user, "user_type")
+			if user_type == "Website User":
+				self.create_member_from_website_user()
+			else:
+				frappe.throw(_("Please select a Member"))
 
-			if not member_name:
-				user = frappe.get_doc('User', frappe.session.user)
-				member = frappe.get_doc(dict(
-					doctype='Member',
-					email=frappe.session.user,
-					membership_type=self.membership_type,
-					member_name=user.get_fullname()
-				)).insert(ignore_permissions=True)
-				member_name = member.name
+		self.validate_membership_period()
 
-			if self.get("__islocal"):
-				self.member = member_name
+	def create_member_from_website_user(self):
+		member_name = frappe.get_value("Member", dict(email_id=frappe.session.user))
 
+		if not member_name:
+			user = frappe.get_doc("User", frappe.session.user)
+			member = frappe.get_doc(dict(
+				doctype="Member",
+				email_id=frappe.session.user,
+				membership_type=self.membership_type,
+				member_name=user.get_fullname()
+			)).insert(ignore_permissions=True)
+			member_name = member.name
+
+		if self.get("__islocal"):
+			self.member = member_name
+
+	def validate_membership_period(self):
 		# get last membership (if active)
-		last_membership = erpnext.get_last_membership()
+		last_membership = erpnext.get_last_membership(self.member)
 
 		# if person applied for offline membership
 		if last_membership and not frappe.session.user == "Administrator":
 			# if last membership does not expire in 30 days, then do not allow to renew
 			if getdate(add_days(last_membership.to_date, -30)) > getdate(nowdate()) :
-				frappe.throw(_('You can only renew if your membership expires within 30 days'))
+				frappe.throw(_("You can only renew if your membership expires within 30 days"))
 
 			self.from_date = add_days(last_membership.to_date, 1)
 		elif frappe.session.user == "Administrator":
@@ -48,17 +59,22 @@
 		else:
 			self.from_date = nowdate()
 
-		if frappe.db.get_single_value("Membership Settings", "billing_cycle") == "Yearly":
+		if frappe.db.get_single_value("Non Profit Settings", "billing_cycle") == "Yearly":
 			self.to_date = add_years(self.from_date, 1)
 		else:
 			self.to_date = add_months(self.from_date, 1)
 
 	def on_payment_authorized(self, status_changed_to=None):
-		if status_changed_to in ("Completed", "Authorized"):
-			self.load_from_db()
-			self.db_set('paid', 1)
+		if status_changed_to not in ("Completed", "Authorized"):
+			return
+		self.load_from_db()
+		self.db_set("paid", 1)
+		settings = frappe.get_doc("Non Profit Settings")
+		if settings.allow_invoicing and settings.automate_membership_invoicing:
+			self.generate_invoice(with_payment_entry=settings.automate_membership_payment_entries, save=True)
 
-	def generate_invoice(self, save=True):
+
+	def generate_invoice(self, save=True, with_payment_entry=False):
 		if not (self.paid or self.currency or self.amount):
 			frappe.throw(_("The payment for this membership is not paid. To generate invoice fill the payment details"))
 
@@ -66,34 +82,65 @@
 			frappe.throw(_("An invoice is already linked to this document"))
 
 		member = frappe.get_doc("Member", self.member)
-		plan = frappe.get_doc("Membership Type", self.membership_type)
-		settings = frappe.get_doc("Membership Settings")
-
 		if not member.customer:
 			frappe.throw(_("No customer linked to member {0}").format(frappe.bold(self.member)))
 
-		if not settings.debit_account:
-			frappe.throw(_("You need to set <b>Debit Account</b> in Membership Settings"))
-
-		if not settings.company:
-			frappe.throw(_("You need to set <b>Default Company</b> for invoicing in Membership Settings"))
+		plan = frappe.get_doc("Membership Type", self.membership_type)
+		settings = frappe.get_doc("Non Profit Settings")
+		self.validate_membership_type_and_settings(plan, settings)
 
 		invoice = make_invoice(self, member, plan, settings)
 		self.invoice = invoice.name
 
+		if with_payment_entry:
+			self.make_payment_entry(settings, invoice)
+
 		if save:
 			self.save()
 
 		return invoice
 
+	def validate_membership_type_and_settings(self, plan, settings):
+		settings_link = get_link_to_form("Membership Type", self.membership_type)
+
+		if not settings.membership_debit_account:
+			frappe.throw(_("You need to set <b>Debit Account</b> in {0}").format(settings_link))
+
+		if not settings.company:
+			frappe.throw(_("You need to set <b>Default Company</b> for invoicing in {0}").format(settings_link))
+
+		if not plan.linked_item:
+			frappe.throw(_("Please set a Linked Item for the Membership Type {0}").format(
+				get_link_to_form("Membership Type", self.membership_type)))
+
+	def make_payment_entry(self, settings, invoice):
+		if not settings.membership_payment_account:
+			frappe.throw(_("You need to set <b>Payment Account</b> for Membership in {0}").format(
+				get_link_to_form("Non Profit Settings", "Non Profit Settings")))
+
+		from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
+		frappe.flags.ignore_account_permission = True
+		pe = get_payment_entry(dt="Sales Invoice", dn=invoice.name, bank_amount=invoice.grand_total)
+		frappe.flags.ignore_account_permission=False
+		pe.paid_to = settings.membership_payment_account
+		pe.reference_no = self.name
+		pe.reference_date = getdate()
+		pe.flags.ignore_mandatory = True
+		pe.save()
+		pe.submit()
+
 	def send_acknowlement(self):
-		settings = frappe.get_doc("Membership Settings")
+		settings = frappe.get_doc("Non Profit Settings")
 		if not settings.send_email:
-			frappe.throw(_("You need to enable <b>Send Acknowledge Email</b> in Membership Settings"))
+			frappe.throw(_("You need to enable <b>Send Acknowledge Email</b> in {0}").format(
+				get_link_to_form("Non Profit Settings", "Non Profit Settings")))
 
 		member = frappe.get_doc("Member", self.member)
+		if not member.email_id:
+			frappe.throw(_("Email address of member {0} is missing").format(frappe.utils.get_link_to_form("Member", self.member)))
+
 		plan = frappe.get_doc("Membership Type", self.membership_type)
-		email = member.email_id if member.email_id else member.email
+		email = member.email_id
 		attachments = [frappe.attach_print("Membership", self.name, print_format=settings.membership_print_format)]
 
 		if self.invoice and settings.send_invoice:
@@ -112,55 +159,65 @@
 		}
 
 		if not frappe.flags.in_test:
-			frappe.enqueue(method=frappe.sendmail, queue='short', timeout=300, is_async=True, **email_args)
+			frappe.enqueue(method=frappe.sendmail, queue="short", timeout=300, is_async=True, **email_args)
 		else:
 			frappe.sendmail(**email_args)
 
 	def generate_and_send_invoice(self):
-		invoice = self.generate_invoice(False)
+		self.generate_invoice(save=False)
 		self.send_acknowlement()
 
+
 def make_invoice(membership, member, plan, settings):
 	invoice = frappe.get_doc({
-		'doctype': 'Sales Invoice',
-		'customer': member.customer,
-		'debit_to': settings.debit_account,
-		'currency': membership.currency,
-		'is_pos': 0,
-		'items': [
+		"doctype": "Sales Invoice",
+		"customer": member.customer,
+		"debit_to": settings.membership_debit_account,
+		"currency": membership.currency,
+		"company": settings.company,
+		"is_pos": 0,
+		"items": [
 			{
-				'item_code': plan.linked_item,
-				'rate': membership.amount,
-				'qty': 1
+				"item_code": plan.linked_item,
+				"rate": membership.amount,
+				"qty": 1
 			}
 		]
 	})
-
-	invoice.insert(ignore_permissions=True)
+	invoice.set_missing_values()
+	invoice.insert()
 	invoice.submit()
 
+	frappe.msgprint(_("Sales Invoice created successfully"))
+
 	return invoice
 
+
 def get_member_based_on_subscription(subscription_id, email):
 	members = frappe.get_all("Member", filters={
-					'subscription_id': subscription_id,
-					'email_id': email
+					"subscription_id": subscription_id,
+					"email_id": email
 				}, order_by="creation desc")
 
 	try:
-		return frappe.get_doc("Member", members[0]['name'])
+		return frappe.get_doc("Member", members[0]["name"])
 	except:
 		return None
 
-def verify_signature(data):
-	signature = frappe.request.headers.get('X-Razorpay-Signature')
 
-	settings = frappe.get_doc("Membership Settings")
-	key = settings.get_webhook_secret()
+def verify_signature(data, endpoint="Membership"):
+	if frappe.flags.in_test or os.environ.get("CI"):
+		return True
+	signature = frappe.request.headers.get("X-Razorpay-Signature")
+
+	settings = frappe.get_doc("Non Profit Settings")
+	key = settings.get_webhook_secret(endpoint)
 
 	controller = frappe.get_doc("Razorpay Settings")
 
 	controller.verify_signature(data, signature, key)
+	frappe.set_user(settings.creation_user)
+
 
 @frappe.whitelist(allow_guest=True)
 def trigger_razorpay_subscription(*args, **kwargs):
@@ -168,18 +225,18 @@
 	try:
 		verify_signature(data)
 	except Exception as e:
-		log = frappe.log_error(e, "Webhook Verification Error")
+		log = frappe.log_error(e, "Membership Webhook Verification Error")
 		notify_failure(log)
-		return { 'status': 'Failed', 'reason': e}
+		return { "status": "Failed", "reason": e}
 
 	if isinstance(data, six.string_types):
 		data = json.loads(data)
 	data = frappe._dict(data)
 
-	subscription = data.payload.get("subscription", {}).get('entity', {})
+	subscription = data.payload.get("subscription", {}).get("entity", {})
 	subscription = frappe._dict(subscription)
 
-	payment = data.payload.get("payment", {}).get('entity', {})
+	payment = data.payload.get("payment", {}).get("entity", {})
 	payment = frappe._dict(payment)
 
 	try:
@@ -189,23 +246,22 @@
 		member = get_member_based_on_subscription(subscription.id, payment.email)
 		if not member:
 			member = create_member(frappe._dict({
-				'fullname': payment.email,
-				'email': payment.email,
-				'plan_id': get_plan_from_razorpay_id(subscription.plan_id)
+				"fullname": payment.email,
+				"email": payment.email,
+				"plan_id": get_plan_from_razorpay_id(subscription.plan_id)
 			}))
 
 			member.subscription_id = subscription.id
 			member.customer_id = payment.customer_id
-			if subscription.notes and type(subscription.notes) == dict:
-				notes = '\n'.join("{}: {}".format(k, v) for k, v in subscription.notes.items())
-				member.add_comment("Comment", notes)
-			elif subscription.notes and type(subscription.notes) == str:
-				member.add_comment("Comment", subscription.notes)
 
+			if subscription.get("notes"):
+				member = get_additional_notes(member, subscription)
 
+		company = get_company_for_memberships()
 		# Update Membership
 		membership = frappe.new_doc("Membership")
 		membership.update({
+			"company": company,
 			"member": member.name,
 			"membership_status": "Current",
 			"membership_type": member.membership_type,
@@ -216,39 +272,90 @@
 			"to_date": datetime.fromtimestamp(subscription.current_end),
 			"amount": payment.amount / 100 # Convert to rupees from paise
 		})
-		membership.insert(ignore_permissions=True)
+		membership.flags.ignore_mandatory = True
+		membership.insert()
 
 		# Update membership values
 		member.subscription_start = datetime.fromtimestamp(subscription.start_at)
 		member.subscription_end = datetime.fromtimestamp(subscription.end_at)
 		member.subscription_activated = 1
-		member.save(ignore_permissions=True)
+		member.flags.ignore_mandatory = True
+		member.save()
+
+		settings = frappe.get_doc("Non Profit Settings")
+		if settings.allow_invoicing and settings.automate_membership_invoicing:
+			membership.generate_invoice(with_payment_entry=settings.automate_membership_payment_entries, save=True)
+
 	except Exception as e:
 		message = "{0}\n\n{1}\n\n{2}: {3}".format(e, frappe.get_traceback(), __("Payment ID"), payment.id)
 		log = frappe.log_error(message, _("Error creating membership entry for {0}").format(member.name))
 		notify_failure(log)
-		return { 'status': 'Failed', 'reason': e}
+		return { "status": "Failed", "reason": e}
 
-	return { 'status': 'Success' }
+	return { "status": "Success" }
+
+
+def get_company_for_memberships():
+	company = frappe.db.get_single_value("Non Profit Settings", "company")
+	if not company:
+		from erpnext.healthcare.setup import get_company
+		company = get_company()
+	return company
+
+
+def get_additional_notes(member, subscription):
+	if type(subscription.notes) == dict:
+		for k, v in subscription.notes.items():
+			notes = "\n".join("{}: {}".format(k, v))
+
+			# extract member name from notes
+			if "name" in k.lower():
+				member.update({
+					"member_name": subscription.notes.get(k)
+				})
+
+			# extract pan number from notes
+			if "pan" in k.lower():
+				member.update({
+					"pan_number": subscription.notes.get(k)
+				})
+
+		member.add_comment("Comment", notes)
+
+	elif type(subscription.notes) == str:
+		member.add_comment("Comment", subscription.notes)
+
+	return member
 
 
 def notify_failure(log):
 	try:
-		content = """Dear System Manager,
-Razorpay webhook for creating renewing membership subscription failed due to some reason. Please check the following error log linked below
+		content = """
+			Dear System Manager,
+			Razorpay webhook for creating renewing membership subscription failed due to some reason.
+			Please check the following error log linked below
+			Error Log: {0}
+			Regards, Administrator
+		""".format(get_link_to_form("Error Log", log.name))
 
-Error Log: {0}
-
-Regards,
-Administrator""".format(get_link_to_form("Error Log", log.name))
 		sendmail_to_system_managers("[Important] [ERPNext] Razorpay membership webhook failed , please check.", content)
 	except:
 		pass
 
+
 def get_plan_from_razorpay_id(plan_id):
-	plan = frappe.get_all("Membership Type", filters={'razorpay_plan_id': plan_id}, order_by="creation desc")
+	plan = frappe.get_all("Membership Type", filters={"razorpay_plan_id": plan_id}, order_by="creation desc")
 
 	try:
-		return plan[0]['name']
+		return plan[0]["name"]
 	except:
 		return None
+
+
+def set_expired_status():
+	frappe.db.sql("""
+		UPDATE
+			`tabMembership` SET `status` = 'Expired'
+		WHERE
+			`status` not in ('Cancelled') AND `to_date` < %s
+		""", (nowdate()))
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership/membership_list.js b/erpnext/non_profit/doctype/membership/membership_list.js
new file mode 100644
index 0000000..a959159
--- /dev/null
+++ b/erpnext/non_profit/doctype/membership/membership_list.js
@@ -0,0 +1,15 @@
+frappe.listview_settings['Membership'] = {
+	get_indicator: function(doc) {
+		if (doc.membership_status == 'New') {
+			return [__('New'), 'blue', 'membership_status,=,New'];
+		} else if (doc.membership_status === 'Current') {
+			return [__('Current'), 'green', 'membership_status,=,Current'];
+		} else if (doc.membership_status === 'Pending') {
+			return [__('Pending'), 'yellow', 'membership_status,=,Pending'];
+		} else if (doc.membership_status === 'Expired') {
+			return [__('Expired'), 'grey', 'membership_status,=,Expired'];
+		} else {
+			return [__('Cancelled'), 'red', 'membership_status,=,Cancelled'];
+		}
+	}
+};
diff --git a/erpnext/non_profit/doctype/membership/test_membership.py b/erpnext/non_profit/doctype/membership/test_membership.py
index b23f406..31da792 100644
--- a/erpnext/non_profit/doctype/membership/test_membership.py
+++ b/erpnext/non_profit/doctype/membership/test_membership.py
@@ -2,8 +2,117 @@
 # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 from __future__ import unicode_literals
-
 import unittest
+import frappe
+import erpnext
+from erpnext.non_profit.doctype.member.member import create_member
+from frappe.utils import nowdate, add_months
 
 class TestMembership(unittest.TestCase):
-	pass
+	def setUp(self):
+		plan = setup_membership()
+
+		# make test member
+		self.member_doc = create_member(frappe._dict({
+				'fullname': "_Test_Member",
+				'email': "_test_member_erpnext@example.com",
+				'plan_id': plan.name
+		}))
+		self.member_doc.make_customer_and_link()
+		self.member = self.member_doc.name
+
+	def test_auto_generate_invoice_and_payment_entry(self):
+		entry = make_membership(self.member)
+
+		# Naive test to see if at all invoice was generated and attached to member
+		# In any case if details were missing, the invoicing would throw an error
+		invoice = entry.generate_invoice(save=True)
+		self.assertEqual(invoice.name, entry.invoice)
+
+	def test_renew_within_30_days(self):
+		# create a membership for two months
+		# Should work fine
+		make_membership(self.member, { "from_date": nowdate() })
+		make_membership(self.member, { "from_date": add_months(nowdate(), 1) })
+
+		from frappe.utils.user import add_role
+		add_role("test@example.com", "Non Profit Manager")
+		frappe.set_user("test@example.com")
+
+		# create next membership with expiry not within 30 days
+		self.assertRaises(frappe.ValidationError, make_membership, self.member, {
+			"from_date": add_months(nowdate(), 2),
+		})
+
+		frappe.set_user("Administrator")
+		# create the same membership but as administrator
+		make_membership(self.member, {
+			"from_date": add_months(nowdate(), 2),
+			"to_date": add_months(nowdate(), 3),
+		})
+
+def set_config(key, value):
+	frappe.db.set_value("Non Profit Settings", None, key, value)
+
+def make_membership(member, payload={}):
+	data = {
+		"doctype": "Membership",
+		"member": member,
+		"membership_status": "Current",
+		"membership_type": "_rzpy_test_milythm",
+		"currency": "INR",
+		"paid": 1,
+		"from_date": nowdate(),
+		"amount": 100
+	}
+	data.update(payload)
+	membership = frappe.get_doc(data)
+	membership.insert(ignore_permissions=True, ignore_if_duplicate=True)
+	return membership
+
+def create_item(item_code):
+	if not frappe.db.exists("Item", item_code):
+		item = frappe.new_doc("Item")
+		item.item_code = item_code
+		item.item_name = item_code
+		item.stock_uom = "Nos"
+		item.description = item_code
+		item.item_group = "All Item Groups"
+		item.is_stock_item = 0
+		item.save()
+	else:
+		item = frappe.get_doc("Item", item_code)
+	return item
+
+def setup_membership():
+	# Get default company
+	company = frappe.get_doc("Company", erpnext.get_default_company())
+
+	# update non profit settings
+	settings = frappe.get_doc("Non Profit Settings")
+	# Enable razorpay
+	settings.enable_razorpay_for_memberships = 1
+	settings.billing_cycle = "Monthly"
+	settings.billing_frequency = 24
+	# Enable invoicing
+	settings.allow_invoicing = 1
+	settings.automate_membership_payment_entries = 1
+	settings.company = company.name
+	settings.donation_company = company.name
+	settings.membership_payment_account = company.default_cash_account
+	settings.membership_debit_account = company.default_receivable_account
+	settings.flags.ignore_mandatory = True
+	settings.save()
+
+	# make test plan
+	if not frappe.db.exists("Membership Type", "_rzpy_test_milythm"):
+		plan = frappe.new_doc("Membership Type")
+		plan.membership_type = "_rzpy_test_milythm"
+		plan.amount = 100
+		plan.razorpay_plan_id = "_rzpy_test_milythm"
+		plan.linked_item = create_item("_Test Item for Non Profit Membership").name
+		plan.insert()
+	else:
+		plan = frappe.get_doc("Membership Type", "_rzpy_test_milythm")
+
+	return plan
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership_settings/membership_settings.js b/erpnext/non_profit/doctype/membership_settings/membership_settings.js
deleted file mode 100644
index 1d89402..0000000
--- a/erpnext/non_profit/doctype/membership_settings/membership_settings.js
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on("Membership Settings", {
-	refresh: function(frm) {
-		if (frm.doc.webhook_secret) {
-			frm.add_custom_button(__("Revoke <Key></Key>"), () => {
-				frm.call("revoke_key").then(() => {
-					frm.refresh();
-				})
-			});
-		}
-
-		frm.set_query('inv_print_format', function(doc) {
-			return {
-				filters: {
-					"doc_type": "Sales Invoice"
-				}
-			};
-		});
-
-		frm.set_query('membership_print_format', function(doc) {
-			return {
-				filters: {
-					"doc_type": "Membership"
-				}
-			};
-		});
-
-		frm.set_query('debit_account', function(doc) {
-			return {
-				filters: {
-					'account_type': 'Receivable',
-					'is_group': 0,
-					'company': frm.doc.company
-				}
-			};
-		});
-
-		let docs_url = "https://docs.erpnext.com/docs/user/manual/en/non_profit/membership";
-
-		frm.set_intro(__("You can learn more about memberships in the manual. ") + `<a href='${docs_url}'>${__('ERPNext Docs')}</a>`, true);
-
-		frm.trigger("add_generate_button");
-		frm.trigger("add_copy_buttonn");
-	},
-
-	add_generate_button: function(frm) {
-		let label;
-
-		if (frm.doc.webhook_secret) {
-			label = __("Regenerate Webhook Secret");
-		} else {
-			label = __("Generate Webhook Secret");
-		}
-		frm.add_custom_button(label, () => {
-			frm.call("generate_webhook_key").then(() => {
-				frm.refresh();
-			});
-		});
-	},
-
-	add_copy_buttonn: function(frm) {
-		if (frm.doc.webhook_secret) {
-			frm.add_custom_button(__("Copy Webhook URL"), () => {
-				frappe.utils.copy_to_clipboard(`https://${frappe.boot.sitename}/api/method/erpnext.non_profit.doctype.membership.membership.trigger_razorpay_subscription`);
-			});
-		}
-	}
-});
diff --git a/erpnext/non_profit/doctype/membership_settings/membership_settings.json b/erpnext/non_profit/doctype/membership_settings/membership_settings.json
deleted file mode 100644
index 5b6bab5..0000000
--- a/erpnext/non_profit/doctype/membership_settings/membership_settings.json
+++ /dev/null
@@ -1,164 +0,0 @@
-{
- "actions": [],
- "creation": "2020-03-29 12:57:03.005120",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "enable_razorpay",
-  "razorpay_settings_section",
-  "billing_cycle",
-  "billing_frequency",
-  "webhook_secret",
-  "column_break_6",
-  "enable_auto_invoicing",
-  "company",
-  "debit_account",
-  "column_break_9",
-  "send_email",
-  "send_invoice",
-  "membership_print_format",
-  "inv_print_format",
-  "email_template"
- ],
- "fields": [
-  {
-   "fieldname": "billing_cycle",
-   "fieldtype": "Select",
-   "label": "Billing Cycle",
-   "options": "Monthly\nYearly"
-  },
-  {
-   "default": "0",
-   "fieldname": "enable_razorpay",
-   "fieldtype": "Check",
-   "label": "Enable RazorPay For Memberships"
-  },
-  {
-   "depends_on": "eval:doc.enable_razorpay",
-   "fieldname": "razorpay_settings_section",
-   "fieldtype": "Section Break",
-   "label": "RazorPay Settings"
-  },
-  {
-   "description": "The number of billing cycles for which the customer should be charged. For example, if a customer is buying a 1-year membership that should be billed on a monthly basis, this value should be 12.",
-   "fieldname": "billing_frequency",
-   "fieldtype": "Int",
-   "label": "Billing Frequency"
-  },
-  {
-   "fieldname": "webhook_secret",
-   "fieldtype": "Password",
-   "label": "Webhook Secret",
-   "read_only": 1
-  },
-  {
-   "fieldname": "column_break_6",
-   "fieldtype": "Section Break",
-   "label": "Invoicing"
-  },
-  {
-   "default": "0",
-   "fieldname": "enable_auto_invoicing",
-   "fieldtype": "Check",
-   "label": "Enable Auto Invoicing",
-   "mandatory_depends_on": "eval:doc.send_invoice"
-  },
-  {
-   "depends_on": "eval:doc.enable_auto_invoicing",
-   "fieldname": "debit_account",
-   "fieldtype": "Link",
-   "label": "Debit Account",
-   "mandatory_depends_on": "eval:doc.enable_auto_invoicing",
-   "options": "Account"
-  },
-  {
-   "fieldname": "column_break_9",
-   "fieldtype": "Column Break"
-  },
-  {
-   "depends_on": "eval:doc.enable_auto_invoicing",
-   "fieldname": "company",
-   "fieldtype": "Link",
-   "label": "Company",
-   "mandatory_depends_on": "eval:doc.enable_auto_invoicing",
-   "options": "Company"
-  },
-  {
-   "default": "0",
-   "depends_on": "eval:doc.enable_auto_invoicing && doc.send_email",
-   "fieldname": "send_invoice",
-   "fieldtype": "Check",
-   "label": "Send Invoice with Email"
-  },
-  {
-   "default": "0",
-   "fieldname": "send_email",
-   "fieldtype": "Check",
-   "label": "Send Membership Acknowledgement"
-  },
-  {
-   "depends_on": "eval: doc.send_invoice",
-   "fieldname": "inv_print_format",
-   "fieldtype": "Link",
-   "label": "Invoice Print Format",
-   "mandatory_depends_on": "eval: doc.send_invoice",
-   "options": "Print Format"
-  },
-  {
-   "depends_on": "eval:doc.send_email",
-   "fieldname": "membership_print_format",
-   "fieldtype": "Link",
-   "label": "Membership Print Format",
-   "options": "Print Format"
-  },
-  {
-   "depends_on": "eval:doc.send_email",
-   "fieldname": "email_template",
-   "fieldtype": "Link",
-   "label": "Email Template",
-   "mandatory_depends_on": "eval:doc.send_email",
-   "options": "Email Template"
-  }
- ],
- "issingle": 1,
- "links": [],
- "modified": "2020-08-05 17:26:37.287395",
- "modified_by": "Administrator",
- "module": "Non Profit",
- "name": "Membership Settings",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "print": 1,
-   "read": 1,
-   "role": "System Manager",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "print": 1,
-   "read": 1,
-   "role": "Non Profit Manager",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "email": 1,
-   "print": 1,
-   "read": 1,
-   "role": "Non Profit Member",
-   "share": 1
-  }
- ],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership_settings/membership_settings.py b/erpnext/non_profit/doctype/membership_settings/membership_settings.py
deleted file mode 100644
index f3b2eee..0000000
--- a/erpnext/non_profit/doctype/membership_settings/membership_settings.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- 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 import _
-from frappe.integrations.utils import get_payment_gateway_controller
-from frappe.model.document import Document
-
-class MembershipSettings(Document):
-	def generate_webhook_key(self):
-		key = frappe.generate_hash(length=20)
-		self.webhook_secret = key
-		self.save()
-
-		frappe.msgprint(
-			_("Here is your webhook secret, this will be shown to you only once.") + "<br><br>" + key,
-			_("Webhook Secret")
-		);
-
-	def revoke_key(self):
-		self.webhook_secret = None;
-		self.save()
-
-	def get_webhook_secret(self):
-		return self.get_password(fieldname="webhook_secret", raise_exception=False)
-
-@frappe.whitelist()
-def get_plans_for_membership(*args, **kwargs):
-	controller = get_payment_gateway_controller("Razorpay")
-	plans = controller.get_plans()
-	return [plan.get("item") for plan in plans.get("items")]
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership_type/membership_type.js b/erpnext/non_profit/doctype/membership_type/membership_type.js
index 43311a2..2f24276 100644
--- a/erpnext/non_profit/doctype/membership_type/membership_type.js
+++ b/erpnext/non_profit/doctype/membership_type/membership_type.js
@@ -2,13 +2,21 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on('Membership Type', {
-	refresh: function(frm) {
-		frappe.db.get_single_value("Membership Settings", "enable_razorpay").then(val => {
+	refresh: function (frm) {
+		frappe.db.get_single_value('Non Profit Settings', 'enable_razorpay_for_memberships').then(val => {
 			if (val) frm.set_df_property('razorpay_plan_id', 'hidden', false);
 		});
 
-		frappe.db.get_single_value("Membership Settings", "enable_auto_invoicing").then(val => {
+		frappe.db.get_single_value('Non Profit Settings', 'allow_invoicing').then(val => {
 			if (val) frm.set_df_property('linked_item', 'hidden', false);
 		});
+
+		frm.set_query('linked_item', () => {
+			return {
+				filters: {
+					is_stock_item: 0
+				}
+			};
+		});
 	}
 });
diff --git a/erpnext/non_profit/doctype/membership_type/membership_type.py b/erpnext/non_profit/doctype/membership_type/membership_type.py
index b95b043..022829b 100644
--- a/erpnext/non_profit/doctype/membership_type/membership_type.py
+++ b/erpnext/non_profit/doctype/membership_type/membership_type.py
@@ -5,9 +5,14 @@
 from __future__ import unicode_literals
 from frappe.model.document import Document
 import frappe
+from frappe import _
 
 class MembershipType(Document):
-	pass
+	def validate(self):
+		if self.linked_item:
+			is_stock_item = frappe.db.get_value("Item", self.linked_item, "is_stock_item")
+			if is_stock_item:
+				frappe.throw(_("The Linked Item should be a service item"))
 
 def get_membership_type(razorpay_id):
 	return frappe.db.exists("Membership Type", {"razorpay_plan_id": razorpay_id})
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/non_profit/doctype/non_profit_settings/__init__.py
similarity index 100%
rename from erpnext/non_profit/doctype/membership_settings/__init__.py
rename to erpnext/non_profit/doctype/non_profit_settings/__init__.py
diff --git a/erpnext/non_profit/doctype/non_profit_settings/non_profit_settings.js b/erpnext/non_profit/doctype/non_profit_settings/non_profit_settings.js
new file mode 100644
index 0000000..cff92b4
--- /dev/null
+++ b/erpnext/non_profit/doctype/non_profit_settings/non_profit_settings.js
@@ -0,0 +1,112 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on("Non Profit Settings", {
+	refresh: function(frm) {
+		frm.set_query("inv_print_format", function() {
+			return {
+				filters: {
+					"doc_type": "Sales Invoice"
+				}
+			};
+		});
+
+		frm.set_query("membership_print_format", function() {
+			return {
+				filters: {
+					"doc_type": "Membership"
+				}
+			};
+		});
+
+		frm.set_query("debit_account", function() {
+			return {
+				filters: {
+					"account_type": "Receivable",
+					"is_group": 0,
+					"company": frm.doc.company
+				}
+			};
+		});
+
+		frm.set_query("membership_payment_account", function () {
+			var account_types = ["Bank", "Cash"];
+			return {
+				filters: {
+					"account_type": ["in", account_types],
+					"is_group": 0,
+					"company": frm.doc.company
+				}
+			};
+		});
+
+		let docs_url = "https://docs.erpnext.com/docs/user/manual/en/non_profit/membership";
+
+		frm.set_intro(__("You can learn more about memberships in the manual. ") + `<a href='${docs_url}'>${__('ERPNext Docs')}</a>`, true);
+		frm.trigger("setup_buttons_for_membership");
+		frm.trigger("setup_buttons_for_donation");
+	},
+
+	setup_buttons_for_membership: function(frm) {
+		let label;
+
+		if (frm.doc.membership_webhook_secret) {
+
+			frm.add_custom_button(__("Copy Webhook URL"), () => {
+				frappe.utils.copy_to_clipboard(`https://${frappe.boot.sitename}/api/method/erpnext.non_profit.doctype.membership.membership.trigger_razorpay_subscription`);
+			}, __("Memberships"));
+
+			frm.add_custom_button(__("Revoke Key"), () => {
+				frm.call("revoke_key",  {
+					key: "membership_webhook_secret"
+				}).then(() => {
+					frm.refresh();
+				});
+			}, __("Memberships"));
+
+			label = __("Regenerate Webhook Secret");
+
+		} else {
+			label = __("Generate Webhook Secret");
+		}
+
+		frm.add_custom_button(label, () => {
+			frm.call("generate_webhook_secret", {
+				field: "membership_webhook_secret"
+			}).then(() => {
+				frm.refresh();
+			});
+		}, __("Memberships"));
+	},
+
+	setup_buttons_for_donation: function(frm) {
+		let label;
+
+		if (frm.doc.donation_webhook_secret) {
+			label = __("Regenerate Webhook Secret");
+
+			frm.add_custom_button(__("Copy Webhook URL"), () => {
+				frappe.utils.copy_to_clipboard(`https://${frappe.boot.sitename}/api/method/erpnext.non_profit.doctype.donation.donation.capture_razorpay_donations`);
+			}, __("Donations"));
+
+			frm.add_custom_button(__("Revoke Key"), () => {
+				frm.call("revoke_key", {
+					key: "donation_webhook_secret"
+				}).then(() => {
+					frm.refresh();
+				});
+			}, __("Donations"));
+
+		} else {
+			label = __("Generate Webhook Secret");
+		}
+
+		frm.add_custom_button(label, () => {
+			frm.call("generate_webhook_secret", {
+				field: "donation_webhook_secret"
+			}).then(() => {
+				frm.refresh();
+			});
+		}, __("Donations"));
+	}
+});
diff --git a/erpnext/non_profit/doctype/non_profit_settings/non_profit_settings.json b/erpnext/non_profit/doctype/non_profit_settings/non_profit_settings.json
new file mode 100644
index 0000000..25ff0c1
--- /dev/null
+++ b/erpnext/non_profit/doctype/non_profit_settings/non_profit_settings.json
@@ -0,0 +1,273 @@
+{
+ "actions": [],
+ "creation": "2020-03-29 12:57:03.005120",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "enable_razorpay_for_memberships",
+  "razorpay_settings_section",
+  "billing_cycle",
+  "billing_frequency",
+  "membership_webhook_secret",
+  "column_break_6",
+  "allow_invoicing",
+  "automate_membership_invoicing",
+  "automate_membership_payment_entries",
+  "company",
+  "membership_debit_account",
+  "membership_payment_account",
+  "column_break_9",
+  "send_email",
+  "send_invoice",
+  "membership_print_format",
+  "inv_print_format",
+  "email_template",
+  "donation_settings_section",
+  "donation_company",
+  "default_donor_type",
+  "donation_webhook_secret",
+  "column_break_22",
+  "automate_donation_payment_entries",
+  "donation_debit_account",
+  "donation_payment_account",
+  "section_break_27",
+  "creation_user"
+ ],
+ "fields": [
+  {
+   "fieldname": "billing_cycle",
+   "fieldtype": "Select",
+   "label": "Billing Cycle",
+   "options": "Monthly\nYearly"
+  },
+  {
+   "depends_on": "eval:doc.enable_razorpay_for_memberships",
+   "fieldname": "razorpay_settings_section",
+   "fieldtype": "Section Break",
+   "label": "RazorPay Settings for Memberships"
+  },
+  {
+   "description": "The number of billing cycles for which the customer should be charged. For example, if a customer is buying a 1-year membership that should be billed on a monthly basis, this value should be 12.",
+   "fieldname": "billing_frequency",
+   "fieldtype": "Int",
+   "label": "Billing Frequency"
+  },
+  {
+   "fieldname": "column_break_6",
+   "fieldtype": "Section Break",
+   "label": "Membership Invoicing"
+  },
+  {
+   "fieldname": "column_break_9",
+   "fieldtype": "Column Break"
+  },
+  {
+   "description": "This company will be set for the Memberships created via webhook.",
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.allow_invoicing && doc.send_email",
+   "fieldname": "send_invoice",
+   "fieldtype": "Check",
+   "label": "Send Invoice with Email"
+  },
+  {
+   "default": "0",
+   "fieldname": "send_email",
+   "fieldtype": "Check",
+   "label": "Send Membership Acknowledgement"
+  },
+  {
+   "depends_on": "eval: doc.send_invoice",
+   "fieldname": "inv_print_format",
+   "fieldtype": "Link",
+   "label": "Invoice Print Format",
+   "mandatory_depends_on": "eval: doc.send_invoice",
+   "options": "Print Format"
+  },
+  {
+   "depends_on": "eval:doc.send_email",
+   "fieldname": "membership_print_format",
+   "fieldtype": "Link",
+   "label": "Membership Print Format",
+   "options": "Print Format"
+  },
+  {
+   "depends_on": "eval:doc.send_email",
+   "fieldname": "email_template",
+   "fieldtype": "Link",
+   "label": "Email Template",
+   "mandatory_depends_on": "eval:doc.send_email",
+   "options": "Email Template"
+  },
+  {
+   "default": "0",
+   "fieldname": "allow_invoicing",
+   "fieldtype": "Check",
+   "label": "Allow Invoicing for Memberships",
+   "mandatory_depends_on": "eval:doc.send_invoice || doc.make_payment_entry"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.allow_invoicing",
+   "description": "Automatically create an invoice when payment is authorized from a web form entry",
+   "fieldname": "automate_membership_invoicing",
+   "fieldtype": "Check",
+   "label": "Automate Invoicing for Web Forms"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.allow_invoicing",
+   "description": "Auto creates Payment Entry for Sales Invoices created for Membership from web forms.",
+   "fieldname": "automate_membership_payment_entries",
+   "fieldtype": "Check",
+   "label": "Automate Payment Entry Creation"
+  },
+  {
+   "default": "0",
+   "fieldname": "enable_razorpay_for_memberships",
+   "fieldtype": "Check",
+   "label": "Enable RazorPay For Memberships"
+  },
+  {
+   "depends_on": "eval:doc.automate_membership_payment_entries",
+   "description": "Account for accepting membership payments",
+   "fieldname": "membership_payment_account",
+   "fieldtype": "Link",
+   "label": "Membership Payment To",
+   "mandatory_depends_on": "eval:doc.automate_membership_payment_entries",
+   "options": "Account"
+  },
+  {
+   "fieldname": "membership_webhook_secret",
+   "fieldtype": "Password",
+   "label": "Membership Webhook Secret",
+   "read_only": 1
+  },
+  {
+   "fieldname": "donation_webhook_secret",
+   "fieldtype": "Password",
+   "label": "Donation Webhook Secret",
+   "read_only": 1
+  },
+  {
+   "depends_on": "automate_donation_payment_entries",
+   "description": "Account for accepting donation payments",
+   "fieldname": "donation_payment_account",
+   "fieldtype": "Link",
+   "label": "Donation Payment To",
+   "mandatory_depends_on": "automate_donation_payment_entries",
+   "options": "Account"
+  },
+  {
+   "default": "0",
+   "description": "Auto creates Payment Entry for Donations created from web forms.",
+   "fieldname": "automate_donation_payment_entries",
+   "fieldtype": "Check",
+   "label": "Automate Donation Payment Entries"
+  },
+  {
+   "depends_on": "eval:doc.allow_invoicing",
+   "fieldname": "membership_debit_account",
+   "fieldtype": "Link",
+   "label": "Debit Account",
+   "mandatory_depends_on": "eval:doc.allow_invoicing",
+   "options": "Account"
+  },
+  {
+   "depends_on": "automate_donation_payment_entries",
+   "fieldname": "donation_debit_account",
+   "fieldtype": "Link",
+   "label": "Debit Account",
+   "mandatory_depends_on": "automate_donation_payment_entries",
+   "options": "Account"
+  },
+  {
+   "description": "This company will be set for the Donations created via webhook.",
+   "fieldname": "donation_company",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
+  {
+   "fieldname": "donation_settings_section",
+   "fieldtype": "Section Break",
+   "label": "Donation Settings"
+  },
+  {
+   "fieldname": "column_break_22",
+   "fieldtype": "Column Break"
+  },
+  {
+   "description": "This Donor Type will be set for the Donor created via Donation web form entry.",
+   "fieldname": "default_donor_type",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Default Donor Type",
+   "options": "Donor Type",
+   "reqd": 1
+  },
+  {
+   "fieldname": "section_break_27",
+   "fieldtype": "Section Break"
+  },
+  {
+   "description": "The user that will be used to create Donations, Memberships, Invoices, and Payment Entries. This user should have the relevant permissions.",
+   "fieldname": "creation_user",
+   "fieldtype": "Link",
+   "label": "Creation User",
+   "options": "User",
+   "reqd": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "issingle": 1,
+ "links": [],
+ "modified": "2021-03-11 10:43:38.124240",
+ "modified_by": "Administrator",
+ "module": "Non Profit",
+ "name": "Non Profit Settings",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "role": "Non Profit Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "role": "Non Profit Member",
+   "share": 1
+  }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/non_profit_settings/non_profit_settings.py b/erpnext/non_profit/doctype/non_profit_settings/non_profit_settings.py
new file mode 100644
index 0000000..108554c
--- /dev/null
+++ b/erpnext/non_profit/doctype/non_profit_settings/non_profit_settings.py
@@ -0,0 +1,36 @@
+# -*- 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 import _
+from frappe.integrations.utils import get_payment_gateway_controller
+from frappe.model.document import Document
+
+class NonProfitSettings(Document):
+	def generate_webhook_secret(self, field="membership_webhook_secret"):
+		key = frappe.generate_hash(length=20)
+		self.set(field, key)
+		self.save()
+
+		secret_for = "Membership" if field == "membership_webhook_secret" else "Donation"
+
+		frappe.msgprint(
+			_("Here is your webhook secret for {0} API, this will be shown to you only once.").format(secret_for) + "<br><br>" + key,
+			_("Webhook Secret")
+		)
+
+	def revoke_key(self, key):
+		self.set(key, None)
+		self.save()
+
+	def get_webhook_secret(self, endpoint="Membership"):
+		fieldname = "membership_webhook_secret" if endpoint == "Membership" else "donation_webhook_secret"
+		return self.get_password(fieldname=fieldname, raise_exception=False)
+
+@frappe.whitelist()
+def get_plans_for_membership(*args, **kwargs):
+	controller = get_payment_gateway_controller("Razorpay")
+	plans = controller.get_plans()
+	return [plan.get("item") for plan in plans.get("items")]
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership_settings/test_membership_settings.py b/erpnext/non_profit/doctype/non_profit_settings/test_non_profit_settings.py
similarity index 79%
copy from erpnext/non_profit/doctype/membership_settings/test_membership_settings.py
copy to erpnext/non_profit/doctype/non_profit_settings/test_non_profit_settings.py
index 2ad7984..3f0ede3 100644
--- a/erpnext/non_profit/doctype/membership_settings/test_membership_settings.py
+++ b/erpnext/non_profit/doctype/non_profit_settings/test_non_profit_settings.py
@@ -6,5 +6,5 @@
 # import frappe
 import unittest
 
-class TestMembershipSettings(unittest.TestCase):
+class TestNonProfitSettings(unittest.TestCase):
 	pass
diff --git a/erpnext/non_profit/workspace/non_profit/non_profit.json b/erpnext/non_profit/workspace/non_profit/non_profit.json
new file mode 100644
index 0000000..2557d77
--- /dev/null
+++ b/erpnext/non_profit/workspace/non_profit/non_profit.json
@@ -0,0 +1,251 @@
+{
+ "category": "Domains",
+ "charts": [],
+ "creation": "2020-03-02 17:23:47.811421",
+ "developer_mode_only": 0,
+ "disable_user_customization": 0,
+ "docstatus": 0,
+ "doctype": "Workspace",
+ "extends_another_page": 0,
+ "hide_custom": 0,
+ "icon": "non-profit",
+ "idx": 0,
+ "is_default": 0,
+ "is_standard": 1,
+ "label": "Non Profit",
+ "links": [
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan Management",
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan Type",
+   "link_to": "Loan Type",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan Application",
+   "link_to": "Loan Application",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan",
+   "link_to": "Loan",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Grant Application",
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Grant Application",
+   "link_to": "Grant Application",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Membership",
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Member",
+   "link_to": "Member",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Membership",
+   "link_to": "Membership",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Membership Type",
+   "link_to": "Membership Type",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Membership Settings",
+   "link_to": "Non Profit Settings",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Volunteer",
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Volunteer",
+   "link_to": "Volunteer",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Volunteer Type",
+   "link_to": "Volunteer Type",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Chapter",
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Chapter",
+   "link_to": "Chapter",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Donation",
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Donor",
+   "link_to": "Donor",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Donor Type",
+   "link_to": "Donor Type",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Donation",
+   "link_to": "Donation",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Tax Exemption Certification (India)",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Tax Exemption 80G Certificate",
+   "link_to": "Tax Exemption 80G Certificate",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  }
+ ],
+ "modified": "2021-03-11 11:38:09.140655",
+ "modified_by": "Administrator",
+ "module": "Non Profit",
+ "name": "Non Profit",
+ "owner": "Administrator",
+ "pin_to_bottom": 0,
+ "pin_to_top": 0,
+ "restrict_to_domain": "Non Profit",
+ "shortcuts": [
+  {
+   "label": "Member",
+   "link_to": "Member",
+   "type": "DocType"
+  },
+  {
+   "label": "Non Profit Settings",
+   "link_to": "Non Profit Settings",
+   "type": "DocType"
+  },
+  {
+   "label": "Membership",
+   "link_to": "Membership",
+   "type": "DocType"
+  },
+  {
+   "label": "Chapter",
+   "link_to": "Chapter",
+   "type": "DocType"
+  },
+  {
+   "label": "Chapter Member",
+   "link_to": "Chapter Member",
+   "type": "DocType"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 5b37b38..20ea509 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -450,7 +450,6 @@
 erpnext.patches.v9_0.add_user_to_child_table_in_pos_profile
 erpnext.patches.v9_0.set_schedule_date_for_material_request_and_purchase_order
 erpnext.patches.v9_0.student_admission_childtable_migrate
-erpnext.patches.v9_0.fix_subscription_next_date #2017-10-23
 erpnext.patches.v9_0.add_healthcare_domain
 erpnext.patches.v9_0.set_variant_item_description
 erpnext.patches.v9_0.set_uoms_in_variant_field
@@ -678,7 +677,7 @@
 erpnext.patches.v12_0.fix_quotation_expired_status
 erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry
 erpnext.patches.v12_0.rename_pos_closing_doctype
-erpnext.patches.v13_0.replace_pos_payment_mode_table
+erpnext.patches.v13_0.replace_pos_payment_mode_table #2020-12-29
 erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22
 erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive
 execute:frappe.reload_doc("HR", "doctype", "Employee Advance")
@@ -712,6 +711,7 @@
 execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation")
 erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll #22-06-2020
 erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings #22-06-2020
+execute:frappe.reload_doc("regional", "doctype", "e_invoice_settings")
 erpnext.patches.v13_0.check_is_income_tax_component #22-06-2020
 erpnext.patches.v13_0.loyalty_points_entry_for_pos_invoice #22-07-2020
 erpnext.patches.v12_0.add_taxjar_integration_field
@@ -736,9 +736,22 @@
 erpnext.patches.v12_0.setup_einvoice_fields #2020-12-02
 erpnext.patches.v13_0.updates_for_multi_currency_payroll
 erpnext.patches.v13_0.update_reason_for_resignation_in_employee
-erpnext.patches.v13_0.update_custom_fields_for_shopify
 execute:frappe.delete_doc("Report", "Quoted Item Comparison")
+erpnext.patches.v13_0.update_member_email_address
+erpnext.patches.v13_0.update_custom_fields_for_shopify
 erpnext.patches.v13_0.updates_for_multi_currency_payroll
 erpnext.patches.v13_0.create_leave_policy_assignment_based_on_employee_current_leave_policy
+erpnext.patches.v13_0.update_pos_closing_entry_in_merge_log
 erpnext.patches.v13_0.add_po_to_global_search
 erpnext.patches.v13_0.update_returned_qty_in_pr_dn
+erpnext.patches.v13_0.create_uae_pos_invoice_fields
+erpnext.patches.v13_0.update_project_template_tasks
+erpnext.patches.v13_0.set_company_in_leave_ledger_entry
+erpnext.patches.v13_0.convert_qi_parameter_to_link_field
+erpnext.patches.v13_0.setup_patient_history_settings_for_standard_doctypes
+erpnext.patches.v13_0.add_naming_series_to_old_projects # 1-02-2021
+erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl
+erpnext.patches.v12_0.add_state_code_for_ladakh
+erpnext.patches.v13_0.update_vehicle_no_reqd_condition
+erpnext.patches.v13_0.setup_fields_for_80g_certificate_and_donation
+erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/add_state_code_for_ladakh.py b/erpnext/patches/v12_0/add_state_code_for_ladakh.py
new file mode 100644
index 0000000..d41101c
--- /dev/null
+++ b/erpnext/patches/v12_0/add_state_code_for_ladakh.py
@@ -0,0 +1,16 @@
+import frappe
+from erpnext.regional.india import states
+
+def execute():
+
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	custom_fields = ['Address-gst_state', 'Tax Category-gst_state']
+
+	# Update options in gst_state custom fields
+	for field in custom_fields:
+		gst_state_field = frappe.get_doc('Custom Field', field)
+		gst_state_field.options = '\n'.join(states)
+		gst_state_field.save()
diff --git a/erpnext/patches/v12_0/setup_einvoice_fields.py b/erpnext/patches/v12_0/setup_einvoice_fields.py
index d078276..2474bc3 100644
--- a/erpnext/patches/v12_0/setup_einvoice_fields.py
+++ b/erpnext/patches/v12_0/setup_einvoice_fields.py
@@ -8,6 +8,7 @@
 	if not company:
 		return
 
+	frappe.reload_doc("custom", "doctype", "custom_field")
 	frappe.reload_doc("regional", "doctype", "e_invoice_settings")
 	custom_fields = {
 		'Sales Invoice': [
diff --git a/erpnext/patches/v13_0/add_naming_series_to_old_projects.py b/erpnext/patches/v13_0/add_naming_series_to_old_projects.py
new file mode 100644
index 0000000..5ed9040
--- /dev/null
+++ b/erpnext/patches/v13_0/add_naming_series_to_old_projects.py
@@ -0,0 +1,13 @@
+from __future__ import unicode_literals
+import frappe
+from frappe.custom.doctype.property_setter.property_setter import make_property_setter, delete_property_setter
+
+def execute():
+	frappe.reload_doc("projects", "doctype", "project")
+
+	frappe.db.sql("""UPDATE `tabProject`
+		SET
+			naming_series = 'PROJ-.####'
+		WHERE
+			naming_series is NULL""")
+
diff --git a/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py b/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py
new file mode 100644
index 0000000..289b6a7
--- /dev/null
+++ b/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py
@@ -0,0 +1,23 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	frappe.reload_doc('stock', 'doctype', 'quality_inspection_parameter')
+
+	# get all distinct parameters from QI readigs table
+	reading_params = frappe.db.get_all("Quality Inspection Reading", fields=["distinct specification"])
+	reading_params = [d.specification for d in reading_params]
+
+	# get all distinct parameters from QI Template as some may be unused in QI
+	template_params = frappe.db.get_all("Item Quality Inspection Parameter", fields=["distinct specification"])
+	template_params = [d.specification for d in template_params]
+
+	params = list(set(reading_params + template_params))
+
+	for parameter in params:
+		if not frappe.db.exists("Quality Inspection Parameter", parameter):
+			frappe.get_doc({
+				"doctype": "Quality Inspection Parameter",
+				"parameter": parameter,
+				"description": parameter
+			}).insert(ignore_permissions=True)
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py b/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py
new file mode 100644
index 0000000..48d5cb4
--- /dev/null
+++ b/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py
@@ -0,0 +1,14 @@
+# Copyright (c) 2019, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+from erpnext.regional.united_arab_emirates.setup import make_custom_fields
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': ['in', ['Saudi Arabia', 'United Arab Emirates']]})
+	if not company:
+		return
+
+	make_custom_fields()
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
new file mode 100644
index 0000000..d968e1f
--- /dev/null
+++ b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
@@ -0,0 +1,63 @@
+import frappe
+from frappe import _
+from frappe.utils import getdate, get_time, today
+from erpnext.stock.stock_ledger import update_entries_after
+from erpnext.accounts.utils import update_gl_entries_after
+
+def execute():
+	for doctype in ('repost_item_valuation', 'stock_entry_detail', 'purchase_receipt_item',
+			'purchase_invoice_item', 'delivery_note_item', 'sales_invoice_item', 'packed_item'):
+		frappe.reload_doc('stock', 'doctype', doctype)
+	frappe.reload_doc('buying', 'doctype', 'purchase_receipt_item_supplied')
+
+	reposting_project_deployed_on = get_creation_time()
+	posting_date = getdate(reposting_project_deployed_on)
+	posting_time = get_time(reposting_project_deployed_on)
+
+	if posting_date == today():
+		return
+
+	frappe.clear_cache()
+	frappe.flags.warehouse_account_map = {}
+
+	data = frappe.db.sql('''
+		SELECT
+			name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time
+		FROM
+			`tabStock Ledger Entry`
+		WHERE
+			creation > %s
+			and is_cancelled = 0
+		ORDER BY timestamp(posting_date, posting_time) asc, creation asc
+	''', reposting_project_deployed_on, as_dict=1)
+
+	frappe.db.auto_commit_on_many_writes = 1
+	print("Reposting Stock Ledger Entries...")
+	total_sle = len(data)
+	i = 0
+	for d in data:
+		update_entries_after({
+			"item_code": d.item_code,
+			"warehouse": d.warehouse,
+			"posting_date": d.posting_date,
+			"posting_time": d.posting_time,
+			"voucher_type": d.voucher_type,
+			"voucher_no": d.voucher_no,
+			"sle_id": d.name
+		}, allow_negative_stock=True)
+
+		i += 1
+		if i%100 == 0:
+			print(i, "/", total_sle)
+
+
+	print("Reposting General Ledger Entries...")
+
+	for row in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
+		update_gl_entries_after(posting_date, posting_time, company=row.name)
+
+	frappe.db.auto_commit_on_many_writes = 0
+
+def get_creation_time():
+	return frappe.db.sql(''' SELECT create_time FROM
+		INFORMATION_SCHEMA.TABLES where TABLE_NAME = "tabRepost Item Valuation" ''', as_list=1)[0][0]
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/rename_membership_settings_to_non_profit_settings.py b/erpnext/patches/v13_0/rename_membership_settings_to_non_profit_settings.py
new file mode 100644
index 0000000..3fa09a7
--- /dev/null
+++ b/erpnext/patches/v13_0/rename_membership_settings_to_non_profit_settings.py
@@ -0,0 +1,22 @@
+from __future__ import unicode_literals
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+def execute():
+	if frappe.db.table_exists("Membership Settings"):
+		frappe.rename_doc("DocType", "Membership Settings", "Non Profit Settings")
+		frappe.reload_doctype("Non Profit Settings", force=True)
+
+	if frappe.db.table_exists("Non Profit Settings"):
+		rename_fields_map = {
+			"enable_invoicing": "allow_invoicing",
+			"create_for_web_forms": "automate_membership_invoicing",
+			"make_payment_entry": "automate_membership_payment_entries",
+			"enable_razorpay": "enable_razorpay_for_memberships",
+			"debit_account": "membership_debit_account",
+			"payment_account": "membership_payment_account",
+			"webhook_secret": "membership_webhook_secret"
+		}
+
+		for old_name, new_name in rename_fields_map.items():
+			rename_field("Non Profit Settings", old_name, new_name)
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/replace_pos_payment_mode_table.py b/erpnext/patches/v13_0/replace_pos_payment_mode_table.py
index 1ca211b..7cb2648 100644
--- a/erpnext/patches/v13_0/replace_pos_payment_mode_table.py
+++ b/erpnext/patches/v13_0/replace_pos_payment_mode_table.py
@@ -6,12 +6,10 @@
 import frappe
 
 def execute():
-	frappe.reload_doc("accounts", "doctype", "POS Payment Method")
+	frappe.reload_doc("accounts", "doctype", "pos_payment_method")
 	pos_profiles = frappe.get_all("POS Profile")
 
 	for pos_profile in pos_profiles:
-		if not pos_profile.get("payments"): return
-
 		payments = frappe.db.sql("""
 			select idx, parentfield, parenttype, parent, mode_of_payment, `default` from `tabSales Invoice Payment` where parent=%s
 		""", pos_profile.name, as_dict=1)
diff --git a/erpnext/patches/v13_0/set_company_in_leave_ledger_entry.py b/erpnext/patches/v13_0/set_company_in_leave_ledger_entry.py
new file mode 100644
index 0000000..66857c4
--- /dev/null
+++ b/erpnext/patches/v13_0/set_company_in_leave_ledger_entry.py
@@ -0,0 +1,7 @@
+import frappe
+
+def execute():
+	frappe.reload_doc('HR', 'doctype', 'Leave Allocation')
+	frappe.reload_doc('HR', 'doctype', 'Leave Ledger Entry')
+	frappe.db.sql("""update `tabLeave Ledger Entry` as lle set company = (select company from `tabEmployee` where employee = lle.employee)""")
+	frappe.db.sql("""update `tabLeave Allocation` as la set company = (select company from `tabEmployee` where employee = la.employee)""")
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/setup_fields_for_80g_certificate_and_donation.py b/erpnext/patches/v13_0/setup_fields_for_80g_certificate_and_donation.py
new file mode 100644
index 0000000..aea53f8
--- /dev/null
+++ b/erpnext/patches/v13_0/setup_fields_for_80g_certificate_and_donation.py
@@ -0,0 +1,16 @@
+import frappe
+from erpnext.regional.india.setup import make_custom_fields
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	make_custom_fields()
+
+	if not frappe.db.exists('Party Type', 'Donor'):
+		frappe.get_doc({
+			'doctype': 'Party Type',
+			'party_type': 'Donor',
+			'account_type': 'Receivable'
+		}).insert(ignore_permissions=True)
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py b/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py
new file mode 100644
index 0000000..de08aa2
--- /dev/null
+++ b/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py
@@ -0,0 +1,13 @@
+from __future__ import unicode_literals
+import frappe
+from erpnext.healthcare.setup import setup_patient_history_settings
+
+def execute():
+	if "Healthcare" not in frappe.get_active_domains():
+		return
+
+	frappe.reload_doc("healthcare", "doctype", "Patient History Settings")
+	frappe.reload_doc("healthcare", "doctype", "Patient History Standard Document Type")
+	frappe.reload_doc("healthcare", "doctype", "Patient History Custom Document Type")
+
+	setup_patient_history_settings()
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/update_member_email_address.py b/erpnext/patches/v13_0/update_member_email_address.py
new file mode 100644
index 0000000..4056f84
--- /dev/null
+++ b/erpnext/patches/v13_0/update_member_email_address.py
@@ -0,0 +1,23 @@
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# MIT License. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+def execute():
+	"""add value to email_id column from email"""
+
+	if frappe.db.has_column("Member", "email"):
+		# Get all members
+		for member in frappe.db.get_all("Member", pluck="name"):
+			# Check if email_id already exists
+			if not frappe.db.get_value("Member", member, "email_id"):
+				# fetch email id from the user linked field email
+				email = frappe.db.get_value("Member", member, "email")
+
+				# Set the value for it
+				frappe.db.set_value("Member", member, "email_id", email)
+
+	if frappe.db.exists("DocType", "Membership Settings"):
+		rename_field("Membership Settings", "enable_auto_invoicing", "enable_invoicing")
diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py
index 561e967..8cf09aa 100644
--- a/erpnext/patches/v13_0/update_old_loans.py
+++ b/erpnext/patches/v13_0/update_old_loans.py
@@ -1,7 +1,7 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
-from frappe.utils import nowdate
+from frappe.utils import nowdate, flt
 from erpnext.accounts.doctype.account.test_account import create_account
 from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans
 from erpnext.loan_management.doctype.loan.loan import make_repayment_entry
@@ -113,15 +113,15 @@
 					interest_paid = 0
 					principal_paid = 0
 
-					if total_interest > entry.interest_amount:
-						interest_paid = entry.interest_amount
+					if flt(total_interest) > flt(entry.interest_amount):
+						interest_paid = flt(entry.interest_amount)
 					else:
-						interest_paid = total_interest
+						interest_paid = flt(total_interest)
 
-					if total_principal > entry.payable_principal_amount:
-						principal_paid = entry.payable_principal_amount
+					if flt(total_principal) > flt(entry.payable_principal_amount):
+						principal_paid = flt(entry.payable_principal_amount)
 					else:
-						principal_paid = total_principal
+						principal_paid = flt(total_principal)
 
 					frappe.db.sql(""" UPDATE `tabLoan Interest Accrual`
 						SET paid_principal_amount = `paid_principal_amount` + %s,
@@ -129,8 +129,8 @@
 						WHERE name = %s""",
 						(principal_paid, interest_paid, entry.name))
 
-					total_principal -= principal_paid
-					total_interest -= interest_paid
+					total_principal = flt(total_principal) - principal_paid
+					total_interest = flt(total_interest) - interest_paid
 
 def create_loan_type(loan, loan_type_name, penalty_account):
 	loan_type_doc = frappe.new_doc('Loan Type')
diff --git a/erpnext/patches/v13_0/update_pos_closing_entry_in_merge_log.py b/erpnext/patches/v13_0/update_pos_closing_entry_in_merge_log.py
new file mode 100644
index 0000000..262e38d
--- /dev/null
+++ b/erpnext/patches/v13_0/update_pos_closing_entry_in_merge_log.py
@@ -0,0 +1,25 @@
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# MIT License. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	frappe.reload_doc("accounts", "doctype", "POS Invoice Merge Log")
+	frappe.reload_doc("accounts", "doctype", "POS Closing Entry")
+	if frappe.db.count('POS Invoice Merge Log'):
+		frappe.db.sql('''
+			UPDATE
+				`tabPOS Invoice Merge Log` log, `tabPOS Invoice Reference` log_ref
+			SET
+				log.pos_closing_entry = (
+					SELECT clo_ref.parent FROM `tabPOS Invoice Reference` clo_ref
+					WHERE clo_ref.pos_invoice = log_ref.pos_invoice
+					AND clo_ref.parenttype = 'POS Closing Entry' LIMIT 1
+				)
+			WHERE
+				log_ref.parent = log.name
+		''')
+
+		frappe.db.sql('''UPDATE `tabPOS Closing Entry` SET status = 'Submitted' where docstatus = 1''')
+		frappe.db.sql('''UPDATE `tabPOS Closing Entry` SET status = 'Cancelled' where docstatus = 2''')
diff --git a/erpnext/patches/v13_0/update_project_template_tasks.py b/erpnext/patches/v13_0/update_project_template_tasks.py
new file mode 100644
index 0000000..8cc27d2
--- /dev/null
+++ b/erpnext/patches/v13_0/update_project_template_tasks.py
@@ -0,0 +1,47 @@
+# Copyright (c) 2019, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	frappe.reload_doc("projects", "doctype", "project_template")
+	frappe.reload_doc("projects", "doctype", "project_template_task")
+	frappe.reload_doc("projects", "doctype", "task")
+
+	# Update property setter status if any
+	property_setter = frappe.db.get_value('Property Setter', {'doc_type': 'Task',
+		'field_name': 'status', 'property': 'options'})
+
+	if property_setter:
+		property_setter_doc = frappe.get_doc('Property Setter', {'doc_type': 'Task',
+			'field_name': 'status', 'property': 'options'})
+		property_setter_doc.value += "\nTemplate"
+		property_setter_doc.save()
+
+	for template_name in frappe.get_all('Project Template'):
+		template = frappe.get_doc("Project Template", template_name.name)
+		replace_tasks = False
+		new_tasks = []
+		for task in template.tasks:
+			if task.subject:
+				replace_tasks = True
+				new_task = frappe.get_doc(dict(
+					doctype = "Task",
+					subject = task.subject,
+					start = task.start,
+					duration = task.duration,
+					task_weight = task.task_weight,
+					description = task.description,
+					is_template = 1
+				)).insert()
+				new_tasks.append(new_task)
+
+		if replace_tasks:
+			template.tasks = []
+			for tsk in new_tasks:
+				template.append("tasks", {
+					"task": tsk.name,
+					"subject": tsk.subject
+				})
+			template.save()
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py
new file mode 100644
index 0000000..c26cddb
--- /dev/null
+++ b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py
@@ -0,0 +1,9 @@
+import frappe
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	if frappe.db.exists('Custom Field', { 'fieldname': 'vehicle_no' }):
+		frappe.db.set_value('Custom Field', { 'fieldname': 'vehicle_no' }, 'mandatory_depends_on', '')
diff --git a/erpnext/patches/v4_0/map_charge_to_taxes_and_charges.py b/erpnext/patches/v4_0/map_charge_to_taxes_and_charges.py
index ad043dd..97e217a 100644
--- a/erpnext/patches/v4_0/map_charge_to_taxes_and_charges.py
+++ b/erpnext/patches/v4_0/map_charge_to_taxes_and_charges.py
@@ -5,11 +5,11 @@
 import frappe
 
 def execute():
-	# udpate sales cycle
+	# update sales cycle
 	for d in ['Sales Invoice', 'Sales Order', 'Quotation', 'Delivery Note']:
 		frappe.db.sql("""update `tab%s` set taxes_and_charges=charge""" % d)
 
-	# udpate purchase cycle
+	# update purchase cycle
 	for d in ['Purchase Invoice', 'Purchase Order', 'Supplier Quotation', 'Purchase Receipt']:
 		frappe.db.sql("""update `tab%s` set taxes_and_charges=purchase_other_charges""" % d)
 	
diff --git a/erpnext/patches/v9_0/fix_subscription_next_date.py b/erpnext/patches/v9_0/fix_subscription_next_date.py
deleted file mode 100644
index 4595c8d..0000000
--- a/erpnext/patches/v9_0/fix_subscription_next_date.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import getdate
-from frappe.automation.doctype.auto_repeat.auto_repeat import get_next_schedule_date
-
-def execute():
-	frappe.reload_doc('accounts', 'doctype', 'subscription')
-	fields = ["name", "reference_doctype", "reference_document",
-			"start_date", "frequency", "repeat_on_day"]
-
-	for d in fields:
-		if not frappe.db.has_column('Subscription', d):
-			return
-
-	doctypes = ('Purchase Order', 'Sales Order', 'Purchase Invoice', 'Sales Invoice')
-	for data in frappe.get_all('Subscription',
-		fields = fields,
-		filters = {'reference_doctype': ('in', doctypes), 'docstatus': 1}):
-
-		recurring_id = frappe.db.get_value(data.reference_doctype, data.reference_document, "recurring_id")
-		if recurring_id:
-			frappe.db.sql("update `tab{0}` set subscription=%s where recurring_id=%s"
-				.format(data.reference_doctype), (data.name, recurring_id))
-
-		date_field = 'transaction_date'
-		if data.reference_doctype in ['Sales Invoice', 'Purchase Invoice']:
-			date_field = 'posting_date'
-
-		start_date = frappe.db.get_value(data.reference_doctype, data.reference_document, date_field)
-
-		if start_date and getdate(start_date) != getdate(data.start_date):
-			last_ref_date = frappe.db.sql("""
-				select {0}
-				from `tab{1}`
-				where subscription=%s and docstatus < 2
-				order by creation desc
-				limit 1
-			""".format(date_field, data.reference_doctype), data.name)[0][0]
-
-			next_schedule_date = get_next_schedule_date(last_ref_date, data.frequency, data.repeat_on_day)
-
-			frappe.db.set_value("Subscription", data.name, {
-				"start_date": start_date,
-				"next_schedule_date": next_schedule_date
-			}, None)
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.js b/erpnext/payroll/doctype/additional_salary/additional_salary.js
index 0784de9..d1ed91f 100644
--- a/erpnext/payroll/doctype/additional_salary/additional_salary.js
+++ b/erpnext/payroll/doctype/additional_salary/additional_salary.js
@@ -13,13 +13,7 @@
 			};
 		});
 
-		if (!frm.doc.currency) return;
-		frm.set_query("salary_component", function() {
-			return {
-				query: "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components",
-				filters: {currency: frm.doc.currency, company: frm.doc.company}
-			};
-		});
+		frm.trigger('set_earning_component');
 	},
 
 	employee: function(frm) {
@@ -51,6 +45,19 @@
 		});
 	},
 
+	company: function(frm) {
+		frm.trigger('set_earning_component');
+	},
+
+	set_earning_component: function(frm) {
+		if (!frm.doc.company) return;
+		frm.set_query("salary_component", function() {
+			return {
+				filters: {type: ["in", ["earning", "deduction"]], company: frm.doc.company}
+			};
+		});
+	},
+
 	get_employee_currency: function(frm) {
 		frappe.call({
 			method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency",
diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json
index 9a5a463..4c45580 100644
--- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json
+++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json
@@ -23,6 +23,7 @@
   "employee_benefits",
   "totals",
   "total_amount",
+  "column_break",
   "pro_rata_dispensed_amount"
  ],
  "fields": [
@@ -139,11 +140,15 @@
    "label": "Company",
    "options": "Company",
    "reqd": 1
+  },
+  {
+   "fieldname": "column_break",
+   "fieldtype": "Column Break"
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2020-11-25 11:49:05.095101",
+ "modified": "2020-12-14 15:52:08.566418",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Employee Benefit Application",
diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.js b/erpnext/payroll/doctype/employee_incentive/employee_incentive.js
index 85d1c54..b2809b1 100644
--- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.js
+++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.js
@@ -10,15 +10,7 @@
 				}
 			};
 		});
-
-		if (!frm.doc.currency) return;
-		frm.set_query("salary_component", function() {
-			return {
-				query: "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components",
-				filters: {type: "earning", currency: frm.doc.currency, company: frm.doc.company}
-			};
-		});
-
+		frm.trigger('set_earning_component');
 	},
 
 	employee: function(frm) {
@@ -45,11 +37,21 @@
 			callback: function(data) {
 				if (data.message) {
 					frm.set_value("company", data.message.company);
+					frm.trigger('set_earning_component');
 				}
 			}
 		});
 	},
 
+	set_earning_component: function(frm) {
+		if (!frm.doc.company) return;
+		frm.set_query("salary_component", function() {
+			return {
+				filters: {type: "earning", company: frm.doc.company}
+			};
+		});
+	},
+
 	get_employee_currency: function(frm) {
 		frappe.call({
 			method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency",
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
index 0609d19..311f352 100644
--- 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
@@ -86,19 +86,21 @@
 
 		self.assertEqual(declaration.total_exemption_amount, 100000)
 
-def create_payroll_period():
-	if not frappe.db.exists("Payroll Period", "_Test Payroll Period"):
+def create_payroll_period(**args):
+	args = frappe._dict(args)
+	name = args.name or "_Test Payroll Period"
+	if not frappe.db.exists("Payroll Period", name):
 		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)
+			name = name,
+			company =  args.company or erpnext.get_default_company(),
+			start_date = args.start_date or date(date.today().year, 1, 1),
+			end_date = args.end_date or date(date.today().year, 12, 31)
 		)).insert()
 		return payroll_period
 	else:
-		return frappe.get_doc("Payroll Period", "_Test Payroll Period")
+		return frappe.get_doc("Payroll Period", name)
 
 def create_exemption_category():
 	if not frappe.db.exists("Employee Tax Exemption Category", "_Test Category"):
diff --git a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py
index 253f023..81e3647 100644
--- a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py
+++ b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py
@@ -3,8 +3,11 @@
 # For license information, please see license.txt
 
 from __future__ import unicode_literals
-# import frappe
+#import frappe
+import erpnext
 from frappe.model.document import Document
 
 class IncomeTaxSlab(Document):
-	pass
+	def validate(self):
+		if self.company:
+			self.currency = erpnext.get_company_currency(self.company)
diff --git a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json
index 8a55224..09c7eb9 100644
--- a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json
+++ b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json
@@ -17,8 +17,7 @@
    "fieldtype": "Link",
    "in_list_view": 1,
    "label": "Employee",
-   "options": "Employee",
-   "read_only": 1
+   "options": "Employee"
   },
   {
    "fetch_from": "employee.employee_name",
@@ -52,7 +51,7 @@
  ],
  "istable": 1,
  "links": [],
- "modified": "2020-09-30 12:40:07.999878",
+ "modified": "2020-12-17 15:43:29.542977",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Payroll Employee Detail",
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js
index cb48abb..7ead0b3 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js
@@ -3,6 +3,8 @@
 
 var in_progress = false;
 
+frappe.provide("erpnext.accounts.dimensions");
+
 frappe.ui.form.on('Payroll Entry', {
 	onload: function (frm) {
 		if (!frm.doc.posting_date) {
@@ -10,15 +12,23 @@
 		}
 		frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet);
 
-		frm.set_query("department", function() {
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
+		frm.events.department_filters(frm);
+		frm.events.payroll_payable_account_filters(frm);
+	},
+
+	department_filters: function (frm) {
+		frm.set_query("department", function () {
 			return {
 				"filters": {
 					"company": frm.doc.company,
 				}
 			};
 		});
+	},
 
-		frm.set_query("payroll_payable_account", function() {
+	payroll_payable_account_filters: function (frm) {
+		frm.set_query("payroll_payable_account", function () {
 			return {
 				filters: {
 					"company": frm.doc.company,
@@ -29,20 +39,20 @@
 		});
 	},
 
-	refresh: function(frm) {
+	refresh: function (frm) {
 		if (frm.doc.docstatus == 0) {
-			if(!frm.is_new()) {
+			if (!frm.is_new()) {
 				frm.page.clear_primary_action();
 				frm.add_custom_button(__("Get Employees"),
-					function() {
+					function () {
 						frm.events.get_employee_details(frm);
 					}
 				).toggleClass('btn-primary', !(frm.doc.employees || []).length);
 			}
-			if ((frm.doc.employees || []).length) {
+			if ((frm.doc.employees || []).length && !frappe.model.has_workflow(frm.doctype)) {
 				frm.page.clear_primary_action();
 				frm.page.set_primary_action(__('Create Salary Slips'), () => {
-					frm.save('Submit').then(()=>{
+					frm.save('Submit').then(() => {
 						frm.page.clear_primary_action();
 						frm.refresh();
 						frm.events.refresh(frm);
@@ -61,48 +71,48 @@
 			doc: frm.doc,
 			method: 'fill_employee_details',
 		}).then(r => {
-			if (r.docs && r.docs[0].employees){
+			if (r.docs && r.docs[0].employees) {
 				frm.employees = r.docs[0].employees;
 				frm.dirty();
 				frm.save();
 				frm.refresh();
-				if(r.docs[0].validate_attendance){
+				if (r.docs[0].validate_attendance) {
 					render_employee_attendance(frm, r.message);
 				}
 			}
-		})
+		});
 	},
 
-	create_salary_slips: function(frm) {
+	create_salary_slips: function (frm) {
 		frm.call({
 			doc: frm.doc,
 			method: "create_salary_slips",
-			callback: function(r) {
+			callback: function () {
 				frm.refresh();
 				frm.toolbar.refresh();
 			}
-		})
+		});
 	},
 
-	add_context_buttons: function(frm) {
-		if(frm.doc.salary_slips_submitted || (frm.doc.__onload && frm.doc.__onload.submitted_ss)) {
+	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() {
+		} 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) {
+	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) {
+			callback: function (r) {
 				if (r.message && !r.message.submitted) {
-					frm.add_custom_button("Make Bank Entry", function() {
+					frm.add_custom_button("Make Bank Entry", function () {
 						make_bank_entry(frm);
 					}).addClass("btn-primary");
 				}
@@ -122,31 +132,43 @@
 					"company": frm.doc.company
 				}
 			};
-		}),
-		frm.set_query("cost_center", function () {
+		});
+
+		frm.set_query('employee', 'employees', () => {
+			if (!frm.doc.company) {
+				frappe.msgprint(__("Please set a Company"));
+				return []
+			}
+			let filters = {};
+			filters['company'] = frm.doc.company;
+			filters['start_date'] = frm.doc.start_date;
+			filters['end_date'] = frm.doc.end_date;
+
+			if (frm.doc.department) {
+				filters['department'] = frm.doc.department;
+			}
+			if (frm.doc.branch) {
+				filters['branch'] = frm.doc.branch;
+			}
+			if (frm.doc.designation) {
+				filters['designation'] = frm.doc.designation;
+			}
 			return {
-				filters: {
-					"is_group": 0,
-					company: frm.doc.company
-				}
-			};
-		}),
-		frm.set_query("project", function () {
-			return {
-				filters: {
-					company: frm.doc.company
-				}
-			};
+				query: "erpnext.payroll.doctype.payroll_entry.payroll_entry.employee_query",
+				filters: filters
+			}
 		});
 	},
 
 	payroll_frequency: function (frm) {
-		frm.trigger("set_start_end_dates");
-		frm.events.clear_employee_table(frm);
+		frm.trigger("set_start_end_dates").then( ()=> {
+			frm.events.clear_employee_table(frm);
+		});
 	},
 
 	company: function (frm) {
 		frm.events.clear_employee_table(frm);
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
 	},
 
 	currency: function (frm) {
@@ -164,17 +186,17 @@
 						from_currency: frm.doc.currency,
 						to_currency: company_currency,
 					},
-					callback: function(r) {
+					callback: function (r) {
 						frm.set_value("exchange_rate", flt(r.message));
 						frm.set_df_property('exchange_rate', 'hidden', 0);
-						frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency
-							+ " = [?] " + company_currency);
+						frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency +
+							" = [?] " + company_currency);
 					}
 				});
 			} else {
 				frm.set_value("exchange_rate", 1.0);
 				frm.set_df_property('exchange_rate', 'hidden', 1);
-				frm.set_df_property("exchange_rate", "description", "" );
+				frm.set_df_property("exchange_rate", "description", "");
 			}
 		}
 	},
@@ -192,9 +214,9 @@
 	},
 
 	start_date: function (frm) {
-		if(!in_progress && frm.doc.start_date){
+		if (!in_progress && frm.doc.start_date) {
 			frm.trigger("set_end_date");
-		}else{
+		} else {
 			// reset flag
 			in_progress = false;
 		}
@@ -228,7 +250,7 @@
 		}
 	},
 
-	set_end_date: function(frm){
+	set_end_date: function (frm) {
 		frappe.call({
 			method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.get_end_date',
 			args: {
@@ -243,19 +265,19 @@
 		});
 	},
 
-	validate_attendance: function(frm){
-		if(frm.doc.validate_attendance && frm.doc.employees){
+	validate_attendance: function (frm) {
+		if (frm.doc.validate_attendance && frm.doc.employees) {
 			frappe.call({
 				method: 'validate_employee_attendance',
 				args: {},
-				callback: function(r) {
+				callback: function (r) {
 					render_employee_attendance(frm, r.message);
 				},
 				doc: frm.doc,
 				freeze: true,
 				freeze_message: __('Validating Employee Attendance...')
 			});
-		}else{
+		} else {
 			frm.fields_dict.attendance_detail_html.html("");
 		}
 	},
@@ -270,18 +292,20 @@
 
 const submit_salary_slip = function (frm) {
 	frappe.confirm(__('This will submit Salary Slips and create accrual Journal Entry. Do you want to proceed?'),
-		function() {
+		function () {
 			frappe.call({
 				method: 'submit_salary_slips',
 				args: {},
-				callback: function() {frm.events.refresh(frm);},
+				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) {
+		function () {
+			if (frappe.dom.freeze_count) {
 				frappe.dom.unfreeze();
 				frm.events.refresh(frm);
 			}
@@ -295,9 +319,11 @@
 		return frappe.call({
 			doc: cur_frm.doc,
 			method: "make_payment_entry",
-			callback: function() {
+			callback: function () {
 				frappe.set_route(
-					'List', 'Journal Entry', {"Journal Entry Account.reference_name": frm.doc.name}
+					'List', 'Journal Entry', {
+						"Journal Entry Account.reference_name": frm.doc.name
+					}
 				);
 			},
 			freeze: true,
@@ -309,11 +335,18 @@
 	}
 };
 
-
-let render_employee_attendance = function(frm, data) {
+let render_employee_attendance = function (frm, data) {
 	frm.fields_dict.attendance_detail_html.html(
 		frappe.render_template('employees_to_mark_attendance', {
 			data: data
 		})
 	);
-}
+};
+
+frappe.ui.form.on('Payroll Employee Detail', {
+	employee: function(frm) {
+		if (!frm.doc.payroll_frequency) {
+			frappe.throw(__("Please set a Payroll Frequency"));
+		}
+	}
+});
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.json b/erpnext/payroll/doctype/payroll_entry/payroll_entry.json
index 7a48dd1..0444134 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.json
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.json
@@ -129,8 +129,7 @@
    "fieldname": "employees",
    "fieldtype": "Table",
    "label": "Employee Details",
-   "options": "Payroll Employee Detail",
-   "read_only": 1
+   "options": "Payroll Employee Detail"
   },
   {
    "fieldname": "section_break_13",
@@ -290,7 +289,7 @@
  "icon": "fa fa-cog",
  "is_submittable": 1,
  "links": [],
- "modified": "2020-10-23 13:00:33.753228",
+ "modified": "2020-12-17 15:13:17.766210",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Payroll Entry",
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
index 8c2d974..b520cda 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
@@ -6,29 +6,43 @@
 import frappe, erpnext
 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.utils import cint, flt, add_days, getdate, add_to_date, DATE_FORMAT, date_diff, comma_and
 from frappe import _
 from erpnext.accounts.utils import get_fiscal_year
 from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
+from frappe.desk.reportview import get_match_cond, get_filters_cond
 
 class PayrollEntry(Document):
 	def onload(self):
 		if not self.docstatus==1 or self.salary_slips_submitted:
-    			return
+				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)
+				self.set_onload("submitted_ss", True)
+
+	def validate(self):
+		self.number_of_employees = len(self.employees)
 
 	def on_submit(self):
 		self.create_salary_slips()
 
 	def before_submit(self):
+		self.validate_employee_details()
 		if self.validate_attendance:
 			if self.validate_employee_attendance():
 				frappe.throw(_("Cannot Submit, Employees left to mark attendance"))
 
+	def validate_employee_details(self):
+		emp_with_sal_slip = []
+		for employee_details in self.employees:
+			if frappe.db.exists("Salary Slip", {"employee": employee_details.employee, "start_date": self.start_date, "end_date": self.end_date, "docstatus": 1}):
+				emp_with_sal_slip.append(employee_details.employee)
+
+		if len(emp_with_sal_slip):
+			frappe.throw(_("Salary Slip already exists for {0} ").format(comma_and(emp_with_sal_slip)))
+
 	def on_cancel(self):
 		frappe.delete_doc("Salary Slip", frappe.db.sql_list("""select name from `tabSalary Slip`
 			where payroll_entry=%s """, (self.name)))
@@ -71,8 +85,17 @@
 					and t2.docstatus = 1
 			%s order by t2.from_date desc
 			""" % cond, {"sal_struct": tuple(sal_struct), "from_date": self.end_date, "payroll_payable_account": self.payroll_payable_account}, as_dict=True)
+
+			emp_list = self.remove_payrolled_employees(emp_list)
 			return emp_list
 
+	def remove_payrolled_employees(self, emp_list):
+		for employee_details in emp_list:
+			if frappe.db.exists("Salary Slip", {"employee": employee_details.employee, "start_date": self.start_date, "end_date": self.end_date, "docstatus": 1}):
+				emp_list.remove(employee_details)
+
+		return emp_list
+
 	def fill_employee_details(self):
 		self.set('employees', [])
 		employees = self.get_emp_list()
@@ -94,7 +117,7 @@
 		for d in employees:
 			self.append('employees', d)
 
-		self.number_of_employees = len(employees)
+		self.number_of_employees = len(self.employees)
 		if self.validate_attendance:
 			return self.validate_employee_attendance()
 
@@ -126,8 +149,8 @@
 		"""
 		self.check_permission('write')
 		self.created = 1
-		emp_list = [d.employee for d in self.get_emp_list()]
-		if emp_list:
+		employees = [emp.employee for emp in self.employees]
+		if employees:
 			args = frappe._dict({
 				"salary_slip_based_on_timesheet": self.salary_slip_based_on_timesheet,
 				"payroll_frequency": self.payroll_frequency,
@@ -141,10 +164,10 @@
 				"exchange_rate": self.exchange_rate,
 				"currency": self.currency
 			})
-			if len(emp_list) > 30:
-				frappe.enqueue(create_salary_slips_for_employees, timeout=600, employees=emp_list, args=args)
+			if len(employees) > 30:
+				frappe.enqueue(create_salary_slips_for_employees, timeout=600, employees=employees, args=args)
 			else:
-				create_salary_slips_for_employees(emp_list, args, publish_progress=False)
+				create_salary_slips_for_employees(employees, args, publish_progress=False)
 				# since this method is called via frm.call this doc needs to be updated manually
 				self.reload()
 
@@ -152,13 +175,12 @@
 		"""
 			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)
+			where t1.docstatus = %s and t1.start_date >= %s and t1.end_date <= %s and t1.payroll_entry = %s
+			and (t1.journal_entry is null or t1.journal_entry = "") and ifnull(salary_slip_based_on_timesheet,0) = %s
+		""", (ss_status, self.start_date, self.end_date, self.name, self.salary_slip_based_on_timesheet), as_dict=as_dict)
 		return ss_list
 
 	def submit_salary_slips(self):
@@ -310,10 +332,9 @@
 	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)
+			where t1.docstatus = 1 and start_date >= %s and end_date <= %s and t1.payroll_entry = %s
+			""", (self.start_date, self.end_date, self.name), as_list = True)
 
 		if salary_slip_name_list and len(salary_slip_name_list) > 0:
 			salary_slip_total = 0
@@ -528,6 +549,7 @@
 def create_salary_slips_for_employees(employees, args, publish_progress=True):
 	salary_slips_exists_for = get_existing_salary_slips(employees, args)
 	count=0
+	salary_slips_not_created = []
 	for emp in employees:
 		if emp not in salary_slips_exists_for:
 			args.update({
@@ -540,26 +562,18 @@
 			if publish_progress:
 				frappe.publish_progress(count*100/len(set(employees) - set(salary_slips_exists_for)),
 					title = _("Creating Salary Slips..."))
-		else:
-			salary_slip_name = frappe.db.sql(
-				'''SELECT 
-						name
-					FROM `tabSalary Slip`
-					WHERE company=%s
-					AND start_date >= %s
-					AND end_date <= %s
-					AND employee = %s
-				''', (args.company, args.start_date, args.end_date, emp), as_dict=True)
 
-			salary_slip_doc = frappe.get_doc('Salary Slip', salary_slip_name[0].name)
-			salary_slip_doc.exchange_rate = args.exchange_rate
-			salary_slip_doc.set_totals()
-			salary_slip_doc.db_update()
+		else:
+			salary_slips_not_created.append(emp)
 
 	payroll_entry = frappe.get_doc("Payroll Entry", args.payroll_entry)
 	payroll_entry.db_set("salary_slips_created", 1)
 	payroll_entry.notify_update()
 
+	if salary_slips_not_created:
+		frappe.msgprint(_("Salary Slips already exists for employees {}, and will not be processed by this payroll.")
+			.format(frappe.bold(", ".join([emp for emp in salary_slips_not_created]))) , title=_("Message"), indicator="orange")
+
 def get_existing_salary_slips(employees, args):
 	return frappe.db.sql_list("""
 		select distinct employee from `tabSalary Slip`
@@ -619,3 +633,57 @@
 			'txt': "%%%s%%" % frappe.db.escape(txt),
 			'start': start, 'page_len': page_len
 		})
+
+def get_employee_with_existing_salary_slip(start_date, end_date):
+
+	return frappe.db.sql_list("""
+		select employee from `tabSalary Slip` 
+		where 
+			(start_date between %(start_date)s and %(end_date)s 
+		or 
+			end_date between %(start_date)s and %(end_date)s 
+		or 
+			%(start_date)s between start_date and end_date)
+		and docstatus = 1
+	""", {'start_date': start_date, 'end_date': end_date})
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
+def employee_query(doctype, txt, searchfield, start, page_len, filters):
+	filters = frappe._dict(filters)
+	conditions = []
+	emp_cond = ''
+	if filters.start_date and filters.end_date:
+		employee_list = get_employee_with_existing_salary_slip(filters.start_date, filters.end_date)
+		filters.pop('start_date')
+		filters.pop('end_date')
+		if employee_list:
+			emp_cond += 'and employee not in %(employee_list)s'
+	else:
+		employee_list = []
+	
+
+	return frappe.db.sql("""select name, employee_name from `tabEmployee`
+		where status = 'Active'
+			and docstatus < 2
+			and ({key} like %(txt)s
+				or employee_name like %(txt)s)
+			{emp_cond}
+			{fcond} {mcond}
+		order by
+			if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
+			if(locate(%(_txt)s, employee_name), locate(%(_txt)s, employee_name), 99999),
+			idx desc,
+			name, employee_name
+		limit %(start)s, %(page_len)s""".format(**{
+			'key': searchfield,
+			'fcond': get_filters_cond(doctype, filters, conditions),
+			'mcond': get_match_cond(doctype),
+			'emp_cond': emp_cond
+		}), {
+			'txt': "%%%s%%" % txt,
+			'_txt': txt.replace("%", ""),
+			'start': start,
+			'page_len': page_len,
+			'employee_list': employee_list
+		})
diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
index 54106c8..9e68df9 100644
--- a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
@@ -22,7 +22,7 @@
 				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"])
+		make_deduction_salary_component(setup=True, test_tax=False, company_list=["_Test Company"])
 
 		frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 0)
 
@@ -41,40 +41,6 @@
 			make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date, payable_account=company_doc.default_payroll_payable_account,
 				currency=company_doc.default_currency)
 
-	def test_multi_currency_payroll_entry(self): # pylint: disable=no-self-use
-		company = erpnext.get_default_company()
-		employee = make_employee("test_muti_currency_employee@payroll.com", company=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)
-
-		company_doc = frappe.get_doc('Company', company)
-		salary_structure = make_salary_structure("_Test Multi Currency Salary Structure", "Monthly", company=company, currency='USD')
-		create_salary_structure_assignment(employee, salary_structure.name, company=company)
-		frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""",(frappe.db.get_value("Employee", {"user_id": "test_muti_currency_employee@payroll.com"})))
-		salary_slip = get_salary_slip("test_muti_currency_employee@payroll.com", "Monthly", "_Test Multi Currency Salary Structure")
-		dates = get_start_end_dates('Monthly', nowdate())
-		payroll_entry = make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date, 
-			payable_account=company_doc.default_payroll_payable_account, currency='USD', exchange_rate=70)
-		payroll_entry.make_payment_entry()
-
-		salary_slip.load_from_db()
-
-		payroll_je = salary_slip.journal_entry
-		payroll_je_doc = frappe.get_doc('Journal Entry', payroll_je)
-
-		self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_debit)
-		self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_credit)
-
-		payment_entry = frappe.db.sql('''
-			Select ifnull(sum(je.total_debit),0) as total_debit, ifnull(sum(je.total_credit),0) as total_credit from `tabJournal Entry` je, `tabJournal Entry Account` jea
-			Where je.name = jea.parent
-			And jea.reference_name = %s
-			''', (payroll_entry.name), as_dict=1)
-
-		self.assertEqual(salary_slip.base_net_pay, payment_entry[0].total_debit)
-		self.assertEqual(salary_slip.base_net_pay, payment_entry[0].total_credit)
 
 	def test_payroll_entry_with_employee_cost_center(self): # pylint: disable=no-self-use
 		for data in frappe.get_all('Salary Component', fields = ["name"]):
@@ -107,9 +73,9 @@
 			frappe.db.get_value("Company", "_Test Company", "default_payroll_payable_account") != "_Test Payroll Payable - _TC":
 				frappe.db.set_value("Company", "_Test Company", "default_payroll_payable_account",
 					"_Test Payroll Payable - _TC")
-
-		make_salary_structure("_Test Salary Structure 1", "Monthly", employee1, company="_Test Company", currency=frappe.db.get_value("Company", "_Test Company", "default_currency"))
-		make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company", currency=frappe.db.get_value("Company", "_Test Company", "default_currency"))
+		currency=frappe.db.get_value("Company", "_Test Company", "default_currency")
+		make_salary_structure("_Test Salary Structure 1", "Monthly", employee1, company="_Test Company", currency=currency, test_tax=False)
+		make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company", currency=currency, test_tax=False)
 
 		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}):
diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period.py b/erpnext/payroll/doctype/payroll_period/payroll_period.py
index d7893d0..46f6cd8 100644
--- a/erpnext/payroll/doctype/payroll_period/payroll_period.py
+++ b/erpnext/payroll/doctype/payroll_period/payroll_period.py
@@ -5,7 +5,7 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
-from frappe.utils import date_diff, getdate, formatdate, cint, month_diff, flt
+from frappe.utils import date_diff, getdate, formatdate, cint, month_diff, flt, add_months
 from frappe.model.document import Document
 from erpnext.hr.utils import get_holidays_for_employee
 
@@ -88,6 +88,8 @@
 		period_start = joining_date
 	if relieving_date and getdate(relieving_date) < getdate(period_end):
 		period_end = relieving_date
+		if month_diff(period_end, start_date) > 1:
+			start_date = add_months(start_date, - (month_diff(period_end, start_date)+1))
 
 	total_sub_periods, remaining_sub_periods = 0.0, 0.0
 
diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.json b/erpnext/payroll/doctype/payroll_settings/payroll_settings.json
index c47caa1..680e518 100644
--- a/erpnext/payroll/doctype/payroll_settings/payroll_settings.json
+++ b/erpnext/payroll/doctype/payroll_settings/payroll_settings.json
@@ -15,6 +15,7 @@
   "daily_wages_fraction_for_half_day",
   "email_salary_slip_to_employee",
   "encrypt_salary_slips_in_emails",
+  "show_leave_balances_in_salary_slip",
   "password_policy"
  ],
  "fields": [
@@ -23,58 +24,44 @@
    "fieldname": "payroll_based_on",
    "fieldtype": "Select",
    "label": "Calculate Payroll Working Days Based On",
-   "options": "Leave\nAttendance",
-   "show_days": 1,
-   "show_seconds": 1
+   "options": "Leave\nAttendance"
   },
   {
    "fieldname": "max_working_hours_against_timesheet",
    "fieldtype": "Float",
-   "label": "Max working hours against Timesheet",
-   "show_days": 1,
-   "show_seconds": 1
+   "label": "Max working hours against Timesheet"
   },
   {
    "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
+   "label": "Include holidays in Total no. of Working Days"
   },
   {
    "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
+   "label": "Disable Rounded Total"
   },
   {
    "fieldname": "column_break_11",
-   "fieldtype": "Column Break",
-   "show_days": 1,
-   "show_seconds": 1
+   "fieldtype": "Column Break"
   },
   {
    "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": "Fraction of Daily Salary for Half Day",
-   "show_days": 1,
-   "show_seconds": 1
+   "label": "Fraction of Daily Salary for Half Day"
   },
   {
    "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
+   "label": "Email Salary Slip to Employee"
   },
   {
    "default": "0",
@@ -82,9 +69,7 @@
    "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
+   "label": "Encrypt Salary Slips in Emails"
   },
   {
    "depends_on": "eval: doc.encrypt_salary_slips_in_emails == 1",
@@ -92,24 +77,27 @@
    "fieldname": "password_policy",
    "fieldtype": "Data",
    "in_list_view": 1,
-   "label": "Password Policy",
-   "show_days": 1,
-   "show_seconds": 1
+   "label": "Password Policy"
   },
   {
    "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
+   "options": "Present\nAbsent"
+  },
+  {
+   "default": "0",
+   "fieldname": "show_leave_balances_in_salary_slip",
+   "fieldtype": "Check",
+   "label": "Show Leave Balances in Salary Slip"
   }
  ],
  "icon": "fa fa-cog",
+ "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2020-06-22 17:00:58.408030",
+ "modified": "2021-02-19 11:07:55.873991",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Payroll Settings",
diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.js b/erpnext/payroll/doctype/retention_bonus/retention_bonus.js
index 6fe8cca..f8bb40a 100644
--- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.js
+++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.js
@@ -4,9 +4,13 @@
 frappe.ui.form.on('Retention Bonus', {
 	setup: function(frm) {
 		frm.set_query("employee", function() {
+			if (!frm.doc.company) {
+				frappe.msgprint(__("Please Select Company First"));
+			}
 			return {
 				filters: {
-					"status": "Active"
+					"status": "Active",
+					"company": frm.doc.company
 				}
 			};
 		});
diff --git a/erpnext/payroll/doctype/salary_detail/salary_detail.json b/erpnext/payroll/doctype/salary_detail/salary_detail.json
index 5c1eb61..393f647 100644
--- a/erpnext/payroll/doctype/salary_detail/salary_detail.json
+++ b/erpnext/payroll/doctype/salary_detail/salary_detail.json
@@ -9,6 +9,7 @@
   "abbr",
   "column_break_3",
   "amount",
+  "year_to_date",
   "section_break_5",
   "additional_salary",
   "statistical_component",
@@ -226,11 +227,19 @@
   {
    "fieldname": "column_break_24",
    "fieldtype": "Column Break"
+  },
+  {
+   "description": "Total salary booked against this component for this employee from the beginning of the year (payroll period or fiscal year) up to the current salary slip's end date.",
+   "fieldname": "year_to_date",
+   "fieldtype": "Currency",
+   "label": "Year To Date",
+   "options": "currency",
+   "read_only": 1
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2020-11-25 13:12:41.081106",
+ "modified": "2021-01-14 13:39:15.847158",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Salary Detail",
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.js b/erpnext/payroll/doctype/salary_slip/salary_slip.js
index abe873d..7460c75 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.js
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.js
@@ -116,7 +116,7 @@
 	},
 
 	exchange_rate: function(frm) {
-		calculate_totals(frm);
+		set_totals(frm);
 	},
 
 	hide_loan_section: function(frm) {
@@ -125,24 +125,24 @@
 
 	change_form_labels: function(frm, company_currency) {
 		frm.set_currency_labels(["base_hour_rate", "base_gross_pay", "base_total_deduction",
-			"base_net_pay", "base_rounded_total", "base_total_in_words"],
+			"base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date"],
 		company_currency);
 
-		frm.set_currency_labels(["hour_rate", "gross_pay", "total_deduction", "net_pay", "rounded_total", "total_in_words"],
+		frm.set_currency_labels(["hour_rate", "gross_pay", "total_deduction", "net_pay", "rounded_total", "total_in_words", "year_to_date", "month_to_date"],
 			frm.doc.currency);
 
 		// toggle fields
 		frm.toggle_display(["exchange_rate", "base_hour_rate", "base_gross_pay", "base_total_deduction",
-			"base_net_pay", "base_rounded_total", "base_total_in_words"],
+			"base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date"],
 		frm.doc.currency != company_currency);
 	},
 
 	change_grid_labels: function(frm) {
-		frm.set_currency_labels(["amount", "default_amount", "additional_amount", "tax_on_flexible_benefit",
-			"tax_on_additional_salary"], frm.doc.currency, "earnings");
+		let fields = ["amount", "year_to_date", "default_amount", "additional_amount", "tax_on_flexible_benefit",
+			"tax_on_additional_salary"];
 
-		frm.set_currency_labels(["amount", "default_amount", "additional_amount", "tax_on_flexible_benefit",
-			"tax_on_additional_salary"], frm.doc.currency, "deductions");
+		frm.set_currency_labels(fields, frm.doc.currency, "earnings");
+		frm.set_currency_labels(fields, frm.doc.currency, "deductions");
 	},
 
 	refresh: function(frm) {
@@ -151,7 +151,6 @@
 		var salary_detail_fields = ["formula", "abbr", "statistical_component", "variable_based_on_taxable_salary"];
 		frm.fields_dict['earnings'].grid.set_column_disp(salary_detail_fields, false);
 		frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields, false);
-		calculate_totals(frm);
 		frm.trigger("set_dynamic_labels");
 	},
 
@@ -206,14 +205,14 @@
 
 frappe.ui.form.on('Salary Slip Timesheet', {
 	time_sheet: function(frm) {
-		calculate_totals(frm);
+		set_totals(frm);
 	},
 	timesheets_remove: function(frm) {
-		calculate_totals(frm);
+		set_totals(frm);
 	}
 });
 
-var calculate_totals = function(frm) {
+var set_totals = function(frm) {
 	if (frm.doc.docstatus === 0) {
 		if (frm.doc.earnings || frm.doc.deductions) {
 			frappe.call({
@@ -229,15 +228,15 @@
 
 frappe.ui.form.on('Salary Detail', {
 	amount: function(frm) {
-		calculate_totals(frm);
+		set_totals(frm);
 	},
 
 	earnings_remove: function(frm) {
-		calculate_totals(frm);
+		set_totals(frm);
 	},
 
 	deductions_remove: function(frm) {
-		calculate_totals(frm);
+		set_totals(frm);
 	},
 
 	salary_component: function(frm, cdt, cdn) {
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json
index 386618c..6688368 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.json
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json
@@ -69,13 +69,19 @@
   "net_pay_info",
   "net_pay",
   "base_net_pay",
+  "year_to_date",
+  "base_year_to_date",
   "column_break_53",
   "rounded_total",
   "base_rounded_total",
+  "month_to_date",
+  "base_month_to_date",
   "section_break_55",
   "total_in_words",
   "column_break_69",
   "base_total_in_words",
+  "leave_details_section",
+  "leave_details",
   "section_break_75",
   "amended_from"
  ],
@@ -578,13 +584,55 @@
   {
    "fieldname": "column_break_69",
    "fieldtype": "Column Break"
+  },
+  {
+   "description": "Total salary booked for this employee from the beginning of the year (payroll period or fiscal year) up to the current salary slip's end date.",
+   "fieldname": "year_to_date",
+   "fieldtype": "Currency",
+   "label": "Year To Date",
+   "options": "currency",
+   "read_only": 1
+  },
+  {
+   "description": "Total salary booked for this employee from the beginning of the month up to the current salary slip's end date.",
+   "fieldname": "month_to_date",
+   "fieldtype": "Currency",
+   "label": "Month To Date",
+   "options": "currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "base_year_to_date",
+   "fieldtype": "Currency",
+   "label": "Year To Date(Company Currency)",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "base_month_to_date",
+   "fieldtype": "Currency",
+   "label": "Month To Date(Company Currency)",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "leave_details_section",
+   "fieldtype": "Section Break",
+   "label": "Leave Details"
+  },
+  {
+   "fieldname": "leave_details",
+   "fieldtype": "Table",
+   "label": "Leave Details",
+   "options": "Salary Slip Leave",
+   "read_only": 1
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 9,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-10-21 23:02:59.400249",
+ "modified": "2021-02-19 11:48:05.383945",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Salary Slip",
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index 20365b1..5c5eccd 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -5,7 +5,7 @@
 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.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, money_in_words, formatdate, get_first_day
 from frappe.model.naming import make_autoname
 
 from frappe import msgprint, _
@@ -18,6 +18,8 @@
 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
+from erpnext.accounts.utils import get_fiscal_year
+from six import iteritems
 
 class SalarySlip(TransactionBase):
 	def __init__(self, *args, **kwargs):
@@ -49,6 +51,10 @@
 			self.get_working_days_details(lwp = self.leave_without_pay)
 
 		self.calculate_net_pay()
+		self.compute_year_to_date()
+		self.compute_month_to_date()
+		self.compute_component_wise_year_to_date()
+		self.add_leave_balances()
 
 		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")
@@ -140,8 +146,8 @@
 				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()
-				payroll_based_on, consider_unmarked_attendance_as = frappe.db.get_value("Payroll Settings", None, ["payroll_based_on","consider_unmarked_attendance_as"])
-				return [payroll_based_on, consider_unmarked_attendance_as]
+				ps = frappe.db.get_value("Payroll Settings", None, ["payroll_based_on","consider_unmarked_attendance_as"], as_dict=1)
+				return [ps.payroll_based_on, ps.consider_unmarked_attendance_as]
 
 	def set_time_sheet(self):
 		if self.salary_slip_based_on_timesheet:
@@ -421,16 +427,19 @@
 	def calculate_net_pay(self):
 		if self.salary_structure:
 			self.calculate_component_amounts("earnings")
-		self.gross_pay = self.get_component_totals("earnings")
+		self.gross_pay = self.get_component_totals("earnings", depends_on_payment_days=1)
 		self.base_gross_pay = flt(flt(self.gross_pay) * flt(self.exchange_rate), self.precision('base_gross_pay'))
 
 		if self.salary_structure:
 			self.calculate_component_amounts("deductions")
-		self.total_deduction = self.get_component_totals("deductions")
-		self.base_total_deduction = flt(flt(self.total_deduction) * flt(self.exchange_rate), self.precision('base_total_deduction'))
 
 		self.set_loan_repayment()
+		self.set_component_amounts_based_on_payment_days()
+		self.set_net_pay()
 
+	def set_net_pay(self):
+		self.total_deduction = self.get_component_totals("deductions")
+		self.base_total_deduction = flt(flt(self.total_deduction) * flt(self.exchange_rate), self.precision('base_total_deduction'))
 		self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment))
 		self.rounded_total = rounded(self.net_pay)
 		self.base_net_pay = flt(flt(self.net_pay) * flt(self.exchange_rate), self.precision('base_net_pay'))
@@ -452,8 +461,6 @@
 		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):
@@ -573,7 +580,7 @@
 					'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,
+					'abbr' : struct_row.abbr or struct_row.get("salary_component_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,
@@ -810,7 +817,7 @@
 			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
+				(relieving_date and 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"))
@@ -943,15 +950,21 @@
 		struct_row['variable_based_on_taxable_salary'] = component.variable_based_on_taxable_salary
 		return struct_row
 
-	def get_component_totals(self, component_type):
+	def get_component_totals(self, component_type, depends_on_payment_days=0):
+		joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
+			["date_of_joining", "relieving_date"])
+
 		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
+				if depends_on_payment_days:
+					amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0]
+				else:
+					amount = flt(d.amount, d.precision("amount"))
+				total += amount
 		return total
 
-	def set_component_amounts_based_on_payment_days(self, component_type):
+	def set_component_amounts_based_on_payment_days(self):
 		joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
 			["date_of_joining", "relieving_date"])
 
@@ -961,8 +974,9 @@
 		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]
+		for component_type in ("earnings", "deductions"):
+			for d in self.get(component_type):
+				d.amount = flt(self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0], d.precision("amount"))
 
 	def set_loan_repayment(self):
 		self.total_loan_repayment = 0
@@ -1086,17 +1100,17 @@
 		self.calculate_net_pay()
 
 	def set_totals(self):
-		self.gross_pay = 0
+		self.gross_pay = 0.0
 		if self.salary_slip_based_on_timesheet == 1:
 			self.calculate_total_for_salary_slip_based_on_timesheet()
 		else:
-			self.total_deduction = 0
-			if self.earnings:
+			self.total_deduction = 0.0
+			if hasattr(self, "earnings"):
 				for earning in self.earnings:
-					self.gross_pay += flt(earning.amount)
-			if self.deductions:
+					self.gross_pay += flt(earning.amount, earning.precision("amount"))
+			if hasattr(self, "deductions"):
 				for deduction in self.deductions:
-					self.total_deduction += flt(deduction.amount)
+					self.total_deduction += flt(deduction.amount, deduction.precision("amount"))
 			self.net_pay = flt(self.gross_pay) - flt(self.total_deduction) - flt(self.total_loan_repayment)
 		self.set_base_totals()
 
@@ -1125,6 +1139,98 @@
 				self.gross_pay += self.earnings[i].amount
 		self.net_pay = flt(self.gross_pay) - flt(self.total_deduction)
 
+	def compute_year_to_date(self):
+		year_to_date = 0
+		period_start_date, period_end_date = self.get_year_to_date_period()
+
+		salary_slip_sum = frappe.get_list('Salary Slip',
+			fields = ['sum(net_pay) as sum'],
+			filters = {'employee_name' : self.employee_name,
+				'start_date' : ['>=', period_start_date],
+				'end_date' : ['<', period_end_date],
+				'name': ['!=', self.name],
+				'docstatus': 1
+			})
+
+		year_to_date = flt(salary_slip_sum[0].sum) if salary_slip_sum else 0.0
+
+		year_to_date += self.net_pay
+		self.year_to_date = year_to_date
+
+	def compute_month_to_date(self):
+		month_to_date = 0
+		first_day_of_the_month = get_first_day(self.start_date)
+		salary_slip_sum = frappe.get_list('Salary Slip',
+			fields = ['sum(net_pay) as sum'],
+			filters = {'employee_name' : self.employee_name,
+				'start_date' : ['>=', first_day_of_the_month],
+				'end_date' : ['<', self.start_date],
+				'name': ['!=', self.name],
+				'docstatus': 1
+			})
+
+		month_to_date = flt(salary_slip_sum[0].sum) if salary_slip_sum else 0.0
+
+		month_to_date += self.net_pay
+		self.month_to_date = month_to_date
+
+	def compute_component_wise_year_to_date(self):
+		period_start_date, period_end_date = self.get_year_to_date_period()
+
+		for key in ('earnings', 'deductions'):
+			for component in self.get(key):
+				year_to_date = 0
+				component_sum = frappe.db.sql("""
+					SELECT sum(detail.amount) as sum
+					FROM `tabSalary Detail` as detail
+					INNER JOIN `tabSalary Slip` as salary_slip
+					ON detail.parent = salary_slip.name
+					WHERE
+						salary_slip.employee_name = %(employee_name)s
+						AND detail.salary_component = %(component)s
+						AND salary_slip.start_date >= %(period_start_date)s
+						AND salary_slip.end_date < %(period_end_date)s
+						AND salary_slip.name != %(docname)s
+						AND salary_slip.docstatus = 1""",
+						{'employee_name': self.employee_name, 'component': component.salary_component, 'period_start_date': period_start_date,
+							'period_end_date': period_end_date, 'docname': self.name}
+				)
+
+				year_to_date = flt(component_sum[0][0]) if component_sum else 0.0
+				year_to_date += component.amount
+				component.year_to_date = year_to_date
+
+	def get_year_to_date_period(self):
+		payroll_period = get_payroll_period(self.start_date, self.end_date, self.company)
+
+		if payroll_period:
+			period_start_date = payroll_period.start_date
+			period_end_date = payroll_period.end_date
+		else:
+			# get dates based on fiscal year if no payroll period exists
+			fiscal_year = get_fiscal_year(date=self.start_date, company=self.company, as_dict=1)
+			period_start_date = fiscal_year.year_start_date
+			period_end_date = fiscal_year.year_end_date
+
+		return period_start_date, period_end_date
+
+	def add_leave_balances(self):
+		self.set('leave_details', [])
+
+		if frappe.db.get_single_value('Payroll Settings', 'show_leave_balances_in_salary_slip'):
+			from erpnext.hr.doctype.leave_application.leave_application import get_leave_details
+			leave_details = get_leave_details(self.employee, self.end_date)
+
+			for leave_type, leave_values in iteritems(leave_details['leave_allocation']):
+				self.append('leave_details', {
+					'leave_type': leave_type,
+					'total_allocated_leaves': flt(leave_values.get('total_leaves')),
+					'expired_leaves': flt(leave_values.get('expired_leaves')),
+					'used_leaves': flt(leave_values.get('leaves_taken')),
+					'pending_leaves': flt(leave_values.get('pending_leaves')),
+					'available_leaves': flt(leave_values.get('remaining_leaves'))
+				})
+
 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))
@@ -1135,4 +1241,4 @@
 
 def generate_password_for_pdf(policy_template, employee):
 	employee = frappe.get_doc("Employee", employee)
-	return policy_template.format(**employee.as_dict())
+	return policy_template.format(**employee.as_dict())
\ 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
index 5daf1d4..f58a8e5 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -9,7 +9,7 @@
 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 frappe.utils import getdate, nowdate, add_days, add_months, flt, get_first_day, get_last_day, cstr
 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
@@ -240,7 +240,11 @@
 			interest_income_account='Interest Income Account - _TC',
 			penalty_income_account='Penalty Income Account - _TC')
 
-		make_salary_structure("Test Loan Repayment Salary Structure", "Monthly", employee=applicant, currency='INR')
+		payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company")
+
+		make_salary_structure("Test Loan Repayment Salary Structure", "Monthly", employee=applicant, currency='INR',
+			payroll_period=payroll_period)
+
 		frappe.db.sql("""delete from `tabLoan""")
 		loan = create_loan(applicant, "Car Loan", 11000, "Repay Over Number of Periods", 20, posting_date=add_months(nowdate(), -1))
 		loan.repay_from_salary = 1
@@ -290,6 +294,65 @@
 		self.assertEqual(salary_slip.gross_pay, 78000)
 		self.assertEqual(salary_slip.base_gross_pay, 78000*70)
 
+	def test_year_to_date_computation(self):
+		from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
+
+		applicant = make_employee("test_ytd@salary.com", company="_Test Company")
+
+		payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company")
+
+		create_tax_slab(payroll_period, allow_tax_exemption=True, currency="INR", effective_date=getdate("2019-04-01"),
+			company="_Test Company")
+
+		salary_structure = make_salary_structure("Monthly Salary Structure Test for Salary Slip YTD",
+			"Monthly", employee=applicant, company="_Test Company", currency="INR", payroll_period=payroll_period)
+
+		# clear salary slip for this employee
+		frappe.db.sql("DELETE FROM `tabSalary Slip` where employee_name = 'test_ytd@salary.com'")
+
+		create_salary_slips_for_payroll_period(applicant, salary_structure.name,
+			payroll_period, deduct_random=False)
+
+		salary_slips = frappe.get_all('Salary Slip', fields=['year_to_date', 'net_pay'], filters={'employee_name':
+			'test_ytd@salary.com'}, order_by = 'posting_date')
+
+		year_to_date = 0
+		for slip in salary_slips:
+			year_to_date += flt(slip.net_pay)
+			self.assertEqual(slip.year_to_date, year_to_date)
+
+	def test_component_wise_year_to_date_computation(self):
+		from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
+
+		applicant = make_employee("test_ytd@salary.com", company="_Test Company")
+
+		payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company")
+
+		create_tax_slab(payroll_period, allow_tax_exemption=True, currency="INR", effective_date=getdate("2019-04-01"),
+			company="_Test Company")
+
+		salary_structure = make_salary_structure("Monthly Salary Structure Test for Salary Slip YTD",
+			"Monthly", employee=applicant, company="_Test Company", currency="INR", payroll_period=payroll_period)
+
+		# clear salary slip for this employee
+		frappe.db.sql("DELETE FROM `tabSalary Slip` where employee_name = 'test_ytd@salary.com'")
+
+		create_salary_slips_for_payroll_period(applicant, salary_structure.name,
+			payroll_period, deduct_random=False, num=3)
+
+		salary_slips = frappe.get_all("Salary Slip", fields=["name"], filters={"employee_name":
+			"test_ytd@salary.com"}, order_by = "posting_date")
+
+		year_to_date = dict()
+		for slip in salary_slips:
+			doc = frappe.get_doc("Salary Slip", slip.name)
+			for entry in doc.get("earnings"):
+				if not year_to_date.get(entry.salary_component):
+					year_to_date[entry.salary_component] = 0
+
+				year_to_date[entry.salary_component] += entry.amount
+				self.assertEqual(year_to_date[entry.salary_component], entry.year_to_date)
+
 	def test_tax_for_payroll_period(self):
 		data = {}
 		# test the impact of tax exemption declaration, tax exemption proof submission
@@ -410,10 +473,7 @@
 		salary_structure = payroll_frequency + " Salary Structure Test for Salary Slip"
 
 	employee = frappe.db.get_value("Employee", {"user_id": user})
-	if not frappe.db.exists('Salary Structure', salary_structure):
-		salary_structure_doc = make_salary_structure(salary_structure, payroll_frequency, employee)
-	else:
-		salary_structure_doc = frappe.get_doc('Salary Structure', salary_structure)
+	salary_structure_doc = make_salary_structure(salary_structure, payroll_frequency, employee=employee)
 	salary_slip_name = frappe.db.get_value("Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})})
 
 	if not salary_slip_name:
@@ -557,14 +617,6 @@
 			"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:
@@ -575,6 +627,15 @@
 			"type": "Deduction",
 			"round_to_the_nearest_integer": 1
 		})
+	else:
+		data.append({
+			"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 setup or test_tax:
 		make_salary_component(data, test_tax, company_list)
 
@@ -631,8 +692,13 @@
 	}).submit()
 	return claim_date
 
-def create_tax_slab(payroll_period, effective_date = None, allow_tax_exemption = False, dont_submit = False, currency=erpnext.get_default_currency()):
-	frappe.db.sql("""delete from `tabIncome Tax Slab`""")
+def create_tax_slab(payroll_period, effective_date = None, allow_tax_exemption = False, dont_submit = False, currency=None,
+	company=None):
+	if not currency:
+		currency = erpnext.get_default_currency()
+
+	if company:
+		currency = erpnext.get_company_currency(company)
 
 	slabs = [
 		{
@@ -652,31 +718,38 @@
 		}
 	]
 
-	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)
-	income_tax_slab.currency = currency
+	income_tax_slab_name = frappe.db.get_value("Income Tax Slab", {"currency": currency})
+	if not income_tax_slab_name:
+		income_tax_slab = frappe.new_doc("Income Tax Slab")
+		income_tax_slab.name = "Tax Slab: " + payroll_period.name + " " + cstr(currency)
+		income_tax_slab.effective_from = effective_date or add_days(payroll_period.start_date, -2)
+		income_tax_slab.company = company or ''
+		income_tax_slab.currency = currency
 
-	if allow_tax_exemption:
-		income_tax_slab.allow_tax_exemption = 1
-		income_tax_slab.standard_tax_exemption_amount = 50000
+		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)
+		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.append("other_taxes_and_charges", {
+			"description": "cess",
+			"percent": 4
+		})
 
-	income_tax_slab.save()
-	if not dont_submit:
-		income_tax_slab.submit()
+		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):
+		return income_tax_slab.name
+	else:
+		return income_tax_slab_name
+
+def create_salary_slips_for_payroll_period(employee, salary_structure, payroll_period, deduct_random=True, num=12):
 	deducted_dates = []
 	i = 0
-	while i < 12:
+	while i < num:
 		slip = frappe.get_doc({"doctype": "Salary Slip", "employee": employee,
 				"salary_structure": salary_structure, "frequency": "Monthly"})
 		if i == 0:
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/payroll/doctype/salary_slip_leave/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/payroll/doctype/salary_slip_leave/__init__.py
diff --git a/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.json b/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.json
new file mode 100644
index 0000000..7ac453b
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.json
@@ -0,0 +1,78 @@
+{
+ "actions": [],
+ "creation": "2021-02-19 11:45:18.173417",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "leave_type",
+  "total_allocated_leaves",
+  "expired_leaves",
+  "used_leaves",
+  "pending_leaves",
+  "available_leaves"
+ ],
+ "fields": [
+  {
+   "fieldname": "leave_type",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Leave Type",
+   "no_copy": 1,
+   "options": "Leave Type",
+   "read_only": 1
+  },
+  {
+   "fieldname": "total_allocated_leaves",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Total Allocated Leave",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "expired_leaves",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Expired Leave",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "used_leaves",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Used Leave",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "pending_leaves",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Pending Leave",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "available_leaves",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Available Leave",
+   "no_copy": 1,
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-02-19 10:47:48.546724",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Salary Slip Leave",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.py b/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.py
new file mode 100644
index 0000000..7a92bf1
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_slip_leave/salary_slip_leave.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class SalarySlipLeave(Document):
+	pass
diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.js b/erpnext/payroll/doctype/salary_structure/salary_structure.js
index 7daae49..1378bf0 100755
--- a/erpnext/payroll/doctype/salary_structure/salary_structure.js
+++ b/erpnext/payroll/doctype/salary_structure/salary_structure.js
@@ -55,26 +55,26 @@
 	},
 
 	set_earning_deduction_component: function(frm) {
-		if(!frm.doc.currency && !frm.doc.company) return;
+		if(!frm.doc.company) return;
 		frm.set_query("salary_component", "earnings", function() {
 			return {
-				query : "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components",
-				filters: {type: "earning", currency: frm.doc.currency, company: frm.doc.company}
+				filters: {type: "earning", company: frm.doc.company}
 			};
 		});
 		frm.set_query("salary_component", "deductions", function() {
 			return {
-				query : "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components",
-				filters: {type: "deduction", currency: frm.doc.currency, company: frm.doc.company}
+				filters: {type: "deduction", company: frm.doc.company}
 			};
 		});
 	},
 
+	company: function(frm) {
+		frm.trigger('set_earning_deduction_component');
+	},
 
 	currency: function(frm) {
 		calculate_totals(frm.doc);
 		frm.trigger("set_dynamic_labels")
-		frm.trigger('set_earning_deduction_component');
 		frm.refresh()
 	},
 
@@ -118,6 +118,7 @@
 		fields_read_only.forEach(function(field) {
 			frappe.meta.get_docfield("Salary Detail", field, frm.doc.name).read_only = 1;
 		});
+		frm.trigger('set_earning_deduction_component');
 	},
 
 	assign_to_employees:function (frm) {
diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.py b/erpnext/payroll/doctype/salary_structure/salary_structure.py
index 877e41d..1712081 100644
--- a/erpnext/payroll/doctype/salary_structure/salary_structure.py
+++ b/erpnext/payroll/doctype/salary_structure/salary_structure.py
@@ -207,17 +207,3 @@
 
 	return list(set([d.employee for d in employees]))
 
-@frappe.whitelist()
-@frappe.validate_and_sanitize_search_inputs
-def get_earning_deduction_components(doctype, txt, searchfield, start, page_len, filters):
-	if len(filters) < 3:
-		return {}
-
-	return frappe.db.sql("""
-		select t1.salary_component
-		from `tabSalary Component` t1, `tabSalary Component Account` t2
-		where t1.salary_component = t2.parent
-		and t1.type = %s 
-		and t2.company = %s
-		order by salary_component
-	""", (filters['type'], filters['company']) )
diff --git a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
index abb6697..f2fb558 100644
--- a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
+++ b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
@@ -114,7 +114,7 @@
 		self.assertEqual(sal_struct.currency, 'USD')
 
 def make_salary_structure(salary_structure, payroll_frequency, employee=None, dont_submit=False, other_details=None,
-	test_tax=False, company=None, currency=erpnext.get_default_currency()):
+	test_tax=False, company=None, currency=erpnext.get_default_currency(), payroll_period=None):
 	if test_tax:
 		frappe.db.sql("""delete from `tabSalary Structure` where name=%s""",(salary_structure))
 
@@ -141,16 +141,24 @@
 
 	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, currency=currency)
+			create_salary_structure_assignment(employee, salary_structure, company=company, currency=currency,
+			payroll_period=payroll_period)
 
 	return salary_structure_doc
 
-def create_salary_structure_assignment(employee, salary_structure, from_date=None, company=None, currency=erpnext.get_default_currency()):
+def create_salary_structure_assignment(employee, salary_structure, from_date=None, company=None, currency=erpnext.get_default_currency(),
+	payroll_period=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, currency=currency)
+	if not payroll_period:
+		payroll_period = create_payroll_period()
+
+	income_tax_slab = frappe.db.get_value("Income Tax Slab", {"currency": currency})
+
+	if not income_tax_slab:
+		income_tax_slab = create_tax_slab(payroll_period, allow_tax_exemption=True, currency=currency)
 
 	salary_structure_assignment = frappe.new_doc("Salary Structure Assignment")
 	salary_structure_assignment.employee = employee
@@ -162,7 +170,7 @@
 	salary_structure_assignment.payroll_payable_account = get_payable_account(company)
 	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.income_tax_slab = income_tax_slab
 	salary_structure_assignment.submit()
 	return salary_structure_assignment
 
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
index dccb5df..a0c3013 100644
--- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
+++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
@@ -43,7 +43,7 @@
 
 	def set_payroll_payable_account(self):
 		if not self.payroll_payable_account:
-			payroll_payable_account = frappe.db.get_value('Company', self.company, 'default_payable_account')
+			payroll_payable_account = frappe.db.get_value('Company', self.company, 'default_payroll_payable_account')
 			if not payroll_payable_account:
 				payroll_payable_account = frappe.db.get_value(
 					"Account", {
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/payroll/print_format/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/payroll/print_format/__init__.py
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/payroll/print_format/salary_slip_with_year_to_date/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/payroll/print_format/salary_slip_with_year_to_date/__init__.py
diff --git a/erpnext/payroll/print_format/salary_slip_with_year_to_date/salary_slip_with_year_to_date.json b/erpnext/payroll/print_format/salary_slip_with_year_to_date/salary_slip_with_year_to_date.json
new file mode 100644
index 0000000..71ba37f
--- /dev/null
+++ b/erpnext/payroll/print_format/salary_slip_with_year_to_date/salary_slip_with_year_to_date.json
@@ -0,0 +1,25 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2021-01-14 09:56:42.393623",
+ "custom_format": 0,
+ "default_print_language": "en",
+ "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\"}, {\"fieldname\": \"employee\", \"print_hide\": 0, \"label\": \"Employee\"}, {\"fieldname\": \"company\", \"print_hide\": 0, \"label\": \"Company\"}, {\"fieldname\": \"employee_name\", \"print_hide\": 0, \"label\": \"Employee Name\"}, {\"fieldname\": \"department\", \"print_hide\": 0, \"label\": \"Department\"}, {\"fieldname\": \"designation\", \"print_hide\": 0, \"label\": \"Designation\"}, {\"fieldname\": \"branch\", \"print_hide\": 0, \"label\": \"Branch\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"start_date\", \"print_hide\": 0, \"label\": \"Start Date\"}, {\"fieldname\": \"end_date\", \"print_hide\": 0, \"label\": \"End Date\"}, {\"fieldname\": \"total_working_days\", \"print_hide\": 0, \"label\": \"Working Days\"}, {\"fieldname\": \"leave_without_pay\", \"print_hide\": 0, \"label\": \"Leave Without Pay\"}, {\"fieldname\": \"payment_days\", \"print_hide\": 0, \"label\": \"Payment Days\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"earnings\", \"print_hide\": 0, \"label\": \"Earnings\", \"visible_columns\": [{\"fieldname\": \"salary_component\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"amount\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"year_to_date\", \"print_width\": \"\", \"print_hide\": 0}]}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"deductions\", \"print_hide\": 0, \"label\": \"Deductions\", \"visible_columns\": [{\"fieldname\": \"salary_component\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"amount\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"year_to_date\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"depends_on_payment_days\", \"print_width\": \"\", \"print_hide\": 0}]}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"gross_pay\", \"print_hide\": 0, \"label\": \"Gross Pay\"}, {\"fieldname\": \"total_deduction\", \"print_hide\": 0, \"label\": \"Total Deduction\"}, {\"fieldname\": \"net_pay\", \"print_hide\": 0, \"label\": \"Net Pay\"}, {\"fieldname\": \"rounded_total\", \"print_hide\": 0, \"label\": \"Rounded Total\"}, {\"fieldname\": \"total_in_words\", \"print_hide\": 0, \"label\": \"Total in words\"}, {\"fieldtype\": \"Section Break\", \"label\": \"net pay info\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"year_to_date\", \"print_hide\": 0, \"label\": \"Year To Date\"}, {\"fieldname\": \"month_to_date\", \"print_hide\": 0, \"label\": \"Month To Date\"}]",
+ "idx": 0,
+ "line_breaks": 0,
+ "modified": "2021-01-14 10:03:45.283725",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Salary Slip with Year to Date",
+ "owner": "Administrator",
+ "print_format_builder": 0,
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_section_headings": 0,
+ "standard": "Yes"
+}
\ No newline at end of file
diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json
index f3cecd9..3cdfcb2 100644
--- a/erpnext/projects/doctype/project/project.json
+++ b/erpnext/projects/doctype/project/project.json
@@ -2,12 +2,13 @@
  "actions": [],
  "allow_import": 1,
  "allow_rename": 1,
- "autoname": "field:project_name",
+ "autoname": "naming_series:",
  "creation": "2013-03-07 11:55:07",
  "doctype": "DocType",
  "document_type": "Setup",
  "engine": "InnoDB",
  "field_order": [
+  "naming_series",
   "project_name",
   "status",
   "project_type",
@@ -440,13 +441,24 @@
    "fieldtype": "Text",
    "label": "Message",
    "mandatory_depends_on": "collect_progress"
+  },
+  {
+   "fieldname": "naming_series",
+   "fieldtype": "Select",
+   "label": "Series",
+   "no_copy": 1,
+   "options": "PROJ-.####",
+   "print_hide": 1,
+   "reqd": 1,
+   "set_only_once": 1
   }
  ],
  "icon": "fa fa-puzzle-piece",
  "idx": 29,
+ "index_web_pages_for_search": 1,
  "links": [],
  "max_attachments": 4,
- "modified": "2020-04-08 22:11:14.552615",
+ "modified": "2020-09-02 11:54:01.223620",
  "modified_by": "Administrator",
  "module": "Projects",
  "name": "Project",
@@ -488,5 +500,6 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "timeline_field": "customer",
+ "title_field": "project_name",
  "track_seen": 1
 }
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index 5bbd29c..60f85b0 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -13,6 +13,7 @@
 from erpnext.hr.doctype.daily_work_summary.daily_work_summary import get_users_email
 from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
 from frappe.model.document import Document
+from erpnext.education.doctype.student_attendance.student_attendance import get_holiday_list
 
 class Project(Document):
 	def get_feed(self):
@@ -54,17 +55,64 @@
 				self.project_type = template.project_type
 
 			# create tasks from template
+			project_tasks = []
+			tmp_task_details = []
 			for task in template.tasks:
-				frappe.get_doc(dict(
-					doctype = 'Task',
-					subject = task.subject,
-					project = self.name,
-					status = 'Open',
-					exp_start_date = add_days(self.expected_start_date, task.start),
-					exp_end_date = add_days(self.expected_start_date, task.start + task.duration),
-					description = task.description,
-					task_weight = task.task_weight
-				)).insert()
+				template_task_details = frappe.get_doc("Task", task.task)
+				tmp_task_details.append(template_task_details)
+				task = self.create_task_from_template(template_task_details)
+				project_tasks.append(task)
+			self.dependency_mapping(tmp_task_details, project_tasks)
+
+	def create_task_from_template(self, task_details):
+		return frappe.get_doc(dict(
+				doctype = 'Task',
+				subject = task_details.subject,
+				project = self.name,
+				status = 'Open',
+				exp_start_date = self.calculate_start_date(task_details),
+				exp_end_date = self.calculate_end_date(task_details),
+				description = task_details.description,
+				task_weight = task_details.task_weight,
+				type = task_details.type,
+				issue = task_details.issue,
+				is_group = task_details.is_group
+			)).insert()
+
+	def calculate_start_date(self, task_details):
+		self.start_date = add_days(self.expected_start_date, task_details.start)
+		self.start_date = update_if_holiday(self.holiday_list, self.start_date)
+		return self.start_date
+
+	def calculate_end_date(self, task_details):
+		self.end_date = add_days(self.start_date, task_details.duration)
+		return update_if_holiday(self.holiday_list, self.end_date)
+
+	def dependency_mapping(self, template_tasks, project_tasks):
+		for template_task in template_tasks:
+			project_task = list(filter(lambda x: x.subject == template_task.subject, project_tasks))[0]
+			project_task = frappe.get_doc("Task", project_task.name)
+			self.check_depends_on_value(template_task, project_task, project_tasks)
+			self.check_for_parent_tasks(template_task, project_task, project_tasks)
+
+	def check_depends_on_value(self, template_task, project_task, project_tasks):
+		if template_task.get("depends_on") and not project_task.get("depends_on"):
+			for child_task in template_task.get("depends_on"):
+				child_task_subject = frappe.db.get_value("Task", child_task.task, "subject")
+				corresponding_project_task = list(filter(lambda x: x.subject == child_task_subject, project_tasks))
+				if len(corresponding_project_task):
+					project_task.append("depends_on",{
+						"task": corresponding_project_task[0].name
+					})
+					project_task.save()
+
+	def check_for_parent_tasks(self, template_task, project_task, project_tasks):
+		if template_task.get("parent_task") and not project_task.get("parent_task"):
+			parent_task_subject = frappe.db.get_value("Task", template_task.get("parent_task"), "subject")
+			corresponding_project_task = list(filter(lambda x: x.subject == parent_task_subject, project_tasks))
+			if len(corresponding_project_task):
+				project_task.parent_task = corresponding_project_task[0].name
+				project_task.save()
 
 	def is_row_updated(self, row, existing_task_data, fields):
 		if self.get("__islocal") or not existing_task_data: return True
@@ -493,3 +541,9 @@
 
 	project.status = status
 	project.save()
+
+def update_if_holiday(holiday_list, date):
+	holiday_list = holiday_list or get_holiday_list()
+	while is_holiday(holiday_list, date):
+		date = add_days(date, 1)
+	return date
diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py
index 0c4f6f1..d85c826 100644
--- a/erpnext/projects/doctype/project/test_project.py
+++ b/erpnext/projects/doctype/project/test_project.py
@@ -7,60 +7,131 @@
 test_records = frappe.get_test_records('Project')
 test_ignore = ["Sales Order"]
 
-from erpnext.projects.doctype.project_template.test_project_template import get_project_template, make_project_template
-from erpnext.projects.doctype.project.project import set_project_status
-
-from frappe.utils import getdate
+from erpnext.projects.doctype.project_template.test_project_template import make_project_template
+from erpnext.projects.doctype.project.project import update_if_holiday
+from erpnext.projects.doctype.task.test_task import create_task
+from frappe.utils import getdate, nowdate, add_days
 
 class TestProject(unittest.TestCase):
-	def test_project_with_template(self):
-		frappe.db.sql('delete from tabTask where project = "Test Project with Template"')
-		frappe.delete_doc('Project', 'Test Project with Template')
+	def test_project_with_template_having_no_parent_and_depend_tasks(self):
+		project_name = "Test Project with Template - No Parent and Dependend Tasks"
+		frappe.db.sql(""" delete from tabTask where project = %s """, project_name)
+		frappe.delete_doc('Project', project_name)
 
-		project = get_project('Test Project with Template')
+		task1 = task_exists("Test Template Task with No Parent and Dependency")
+		if not task1:
+			task1 = create_task(subject="Test Template Task with No Parent and Dependency", is_template=1, begin=5, duration=3)
 
-		tasks = frappe.get_all('Task', '*', dict(project=project.name), order_by='creation asc')
+		template = make_project_template("Test Project Template - No Parent and Dependend Tasks", [task1])
+		project = get_project(project_name, template)
+		tasks = frappe.get_all('Task', ['subject','exp_end_date','depends_on_tasks'], dict(project=project.name), order_by='creation asc')
 
-		task1 = tasks[0]
-		self.assertEqual(task1.subject, 'Task 1')
-		self.assertEqual(task1.description, 'Task 1 description')
-		self.assertEqual(getdate(task1.exp_start_date), getdate('2019-01-01'))
-		self.assertEqual(getdate(task1.exp_end_date), getdate('2019-01-04'))
+		self.assertEqual(tasks[0].subject, 'Test Template Task with No Parent and Dependency')
+		self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 5, 3))
+		self.assertEqual(len(tasks), 1)
 
-		self.assertEqual(len(tasks), 4)
-		task4 = tasks[3]
-		self.assertEqual(task4.subject, 'Task 4')
-		self.assertEqual(getdate(task4.exp_end_date), getdate('2019-01-06'))
+	def test_project_template_having_parent_child_tasks(self):
+		project_name = "Test Project with Template - Tasks with Parent-Child Relation"
+		frappe.db.sql(""" delete from tabTask where project = %s """, project_name)
+		frappe.delete_doc('Project', project_name)
 
-def get_project(name):
-	template = get_project_template()
+		task1 = task_exists("Test Template Task Parent")
+		if not task1:
+			task1 = create_task(subject="Test Template Task Parent", is_group=1, is_template=1, begin=1, duration=1)
+
+		task2 = task_exists("Test Template Task Child 1")
+		if not task2:
+			task2 = create_task(subject="Test Template Task Child 1", parent_task=task1.name, is_template=1, begin=1, duration=3)
+
+		task3 = task_exists("Test Template Task Child 2")
+		if not task3:
+			task3 = create_task(subject="Test Template Task Child 2", parent_task=task1.name, is_template=1, begin=2, duration=3)
+
+		template = make_project_template("Test Project Template  - Tasks with Parent-Child Relation", [task1, task2, task3])
+		project = get_project(project_name, template)
+		tasks = frappe.get_all('Task', ['subject','exp_end_date','depends_on_tasks', 'name', 'parent_task'], dict(project=project.name), order_by='creation asc')
+
+		self.assertEqual(tasks[0].subject, 'Test Template Task Parent')
+		self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 1, 1))
+
+		self.assertEqual(tasks[1].subject, 'Test Template Task Child 1')
+		self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, 1, 3))
+		self.assertEqual(tasks[1].parent_task, tasks[0].name)
+
+		self.assertEqual(tasks[2].subject, 'Test Template Task Child 2')
+		self.assertEqual(getdate(tasks[2].exp_end_date), calculate_end_date(project, 2, 3))
+		self.assertEqual(tasks[2].parent_task, tasks[0].name)
+
+		self.assertEqual(len(tasks), 3)
+
+	def test_project_template_having_dependent_tasks(self):
+		project_name = "Test Project with Template - Dependent Tasks"
+		frappe.db.sql(""" delete from tabTask where project = %s  """, project_name)
+		frappe.delete_doc('Project', project_name)
+
+		task1 = task_exists("Test Template Task for Dependency")
+		if not task1:
+			task1 = create_task(subject="Test Template Task for Dependency", is_template=1, begin=3, duration=1)
+
+		task2 = task_exists("Test Template Task with Dependency")
+		if not task2:
+			task2 = create_task(subject="Test Template Task with Dependency", depends_on=task1.name, is_template=1, begin=2, duration=2)
+
+		template = make_project_template("Test Project with Template - Dependent Tasks", [task1, task2])
+		project = get_project(project_name, template)
+		tasks = frappe.get_all('Task', ['subject','exp_end_date','depends_on_tasks', 'name'], dict(project=project.name), order_by='creation asc')
+
+		self.assertEqual(tasks[1].subject, 'Test Template Task with Dependency')
+		self.assertEqual(getdate(tasks[1].exp_end_date), calculate_end_date(project, 2, 2))
+		self.assertTrue(tasks[1].depends_on_tasks.find(tasks[0].name) >= 0 )
+
+		self.assertEqual(tasks[0].subject, 'Test Template Task for Dependency')
+		self.assertEqual(getdate(tasks[0].exp_end_date), calculate_end_date(project, 3, 1) )
+
+		self.assertEqual(len(tasks), 2)
+
+def get_project(name, template):
 
 	project = frappe.get_doc(dict(
 		doctype = 'Project',
 		project_name = name,
 		status = 'Open',
 		project_template = template.name,
-		expected_start_date = '2019-01-01'
+		expected_start_date = nowdate()
 	)).insert()
 
 	return project
 
 def make_project(args):
 	args = frappe._dict(args)
-	if args.project_template_name:
-		template = make_project_template(args.project_template_name)
-	else:
-		template = get_project_template()
+
+	if args.project_name and frappe.db.exists("Project", {"project_name": args.project_name}):
+		return frappe.get_doc("Project", {"project_name": args.project_name})
 
 	project = frappe.get_doc(dict(
 		doctype = 'Project',
 		project_name = args.project_name,
 		status = 'Open',
-		project_template = template.name,
 		expected_start_date = args.start_date
 	))
 
-	if not frappe.db.exists("Project", args.project_name):
-		project.insert()
+	if args.project_template_name:
+		template = make_project_template(args.project_template_name)
+		project.project_template = template.name
 
-	return project
\ No newline at end of file
+	project.insert()
+
+	return project
+
+def task_exists(subject):
+	result = frappe.db.get_list("Task", filters={"subject": subject},fields=["name"])
+	if not len(result):
+		return False
+	return frappe.get_doc("Task", result[0].name)
+
+def calculate_end_date(project, start, duration):
+	start = add_days(project.expected_start_date, start)
+	start = update_if_holiday(project.holiday_list, start)
+	end = add_days(start, duration)
+	end = update_if_holiday(project.holiday_list, end)
+	return getdate(end)
\ No newline at end of file
diff --git a/erpnext/projects/doctype/project_template/project_template.js b/erpnext/projects/doctype/project_template/project_template.js
index d7a876d..3d3c15c 100644
--- a/erpnext/projects/doctype/project_template/project_template.js
+++ b/erpnext/projects/doctype/project_template/project_template.js
@@ -5,4 +5,23 @@
 	// refresh: function(frm) {
 
 	// }
+	setup: function (frm) {
+		frm.set_query("task", "tasks", function () {
+			return {
+				filters: {
+					"is_template": 1
+				}
+			};
+		});
+	}
+});
+
+frappe.ui.form.on('Project Template Task', {
+	task: function (frm, cdt, cdn) {
+		var row = locals[cdt][cdn];
+		frappe.db.get_value("Task", row.task, "subject", (value) => {
+			row.subject = value.subject;
+			refresh_field("tasks");
+		});
+	}
 });
diff --git a/erpnext/projects/doctype/project_template/project_template.py b/erpnext/projects/doctype/project_template/project_template.py
index ac78135..aace402 100644
--- a/erpnext/projects/doctype/project_template/project_template.py
+++ b/erpnext/projects/doctype/project_template/project_template.py
@@ -3,8 +3,28 @@
 # For license information, please see license.txt
 
 from __future__ import unicode_literals
-# import frappe
+import frappe
 from frappe.model.document import Document
+from frappe import _
+from frappe.utils import get_link_to_form
 
 class ProjectTemplate(Document):
-	pass
+
+	def validate(self):
+		self.validate_dependencies()
+
+	def validate_dependencies(self):
+		for task in self.tasks:
+			task_details = frappe.get_doc("Task", task.task)
+			if task_details.depends_on:
+				for dependency_task in task_details.depends_on:
+					if not self.check_dependent_task_presence(dependency_task.task):
+						task_details_format = get_link_to_form("Task",task_details.name)
+						dependency_task_format = get_link_to_form("Task", dependency_task.task)
+						frappe.throw(_("Task {0} depends on Task {1}. Please add Task {1} to the Tasks list.").format(frappe.bold(task_details_format), frappe.bold(dependency_task_format)))
+	
+	def check_dependent_task_presence(self, task):
+		for task_details in self.tasks:
+			if task_details.task == task:
+				return True
+		return False
diff --git a/erpnext/projects/doctype/project_template/test_project_template.py b/erpnext/projects/doctype/project_template/test_project_template.py
index 2c5831a..95663cd 100644
--- a/erpnext/projects/doctype/project_template/test_project_template.py
+++ b/erpnext/projects/doctype/project_template/test_project_template.py
@@ -5,44 +5,25 @@
 
 import frappe
 import unittest
+from erpnext.projects.doctype.task.test_task import create_task
 
 class TestProjectTemplate(unittest.TestCase):
 	pass
 
-def get_project_template():
-	if not frappe.db.exists('Project Template', 'Test Project Template'):
-		frappe.get_doc(dict(
-			doctype = 'Project Template',
-			name = 'Test Project Template',
-			tasks = [
-				dict(subject='Task 1', description='Task 1 description',
-					start=0, duration=3),
-				dict(subject='Task 2', description='Task 2 description',
-					start=0, duration=2),
-				dict(subject='Task 3', description='Task 3 description',
-					start=2, duration=4),
-				dict(subject='Task 4', description='Task 4 description',
-					start=3, duration=2),
-			]
-		)).insert()
-
-	return frappe.get_doc('Project Template', 'Test Project Template')
-
 def make_project_template(project_template_name, project_tasks=[]):
 	if not frappe.db.exists('Project Template', project_template_name):
-		frappe.get_doc(dict(
-			doctype = 'Project Template',
-			name = project_template_name,
-			tasks = project_tasks or [
-				dict(subject='Task 1', description='Task 1 description',
-					start=0, duration=3),
-				dict(subject='Task 2', description='Task 2 description',
-					start=0, duration=2),
-				dict(subject='Task 3', description='Task 3 description',
-					start=2, duration=4),
-				dict(subject='Task 4', description='Task 4 description',
-					start=3, duration=2),
+		project_tasks = project_tasks or [
+				create_task(subject="_Test Template Task 1", is_template=1, begin=0, duration=3),
+				create_task(subject="_Test Template Task 2", is_template=1, begin=0, duration=2),
 			]
-		)).insert()
+		doc = frappe.get_doc(dict(
+			doctype = 'Project Template',
+			name = project_template_name
+		))
+		for task in project_tasks:
+			doc.append("tasks",{
+				"task": task.name
+			})
+		doc.insert()
 
 	return frappe.get_doc('Project Template', project_template_name)
\ No newline at end of file
diff --git a/erpnext/projects/doctype/project_template_task/project_template_task.json b/erpnext/projects/doctype/project_template_task/project_template_task.json
index 8644d89..69530b1 100644
--- a/erpnext/projects/doctype/project_template_task/project_template_task.json
+++ b/erpnext/projects/doctype/project_template_task/project_template_task.json
@@ -1,203 +1,41 @@
 {
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
+ "actions": [],
  "creation": "2019-02-18 17:24:41.830096",
- "custom": 0,
- "docstatus": 0,
  "doctype": "DocType",
- "document_type": "",
  "editable_grid": 1,
  "engine": "InnoDB",
+ "field_order": [
+  "task",
+  "subject"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
+   "columns": 2,
+   "fieldname": "task",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Task",
+   "options": "Task",
+   "reqd": 1
+  },
+  {
+   "columns": 6,
    "fieldname": "subject",
-   "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
+   "fieldtype": "Read Only",
    "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Subject",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "start",
-   "fieldtype": "Int",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Begin On (Days)",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "duration",
-   "fieldtype": "Int",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Duration (Days)",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "task_weight",
-   "fieldtype": "Float",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Task Weight",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "description",
-   "fieldtype": "Text Editor",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Description",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
+   "label": "Subject"
   }
  ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
  "istable": 1,
- "max_attachments": 0,
- "modified": "2019-02-18 18:30:22.688966",
+ "links": [],
+ "modified": "2021-01-07 15:13:40.995071",
  "modified_by": "Administrator",
  "module": "Projects",
  "name": "Project Template Task",
- "name_case": "",
  "owner": "Administrator",
  "permissions": [],
  "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
  "sort_field": "modified",
  "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
+ "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json
index 27f1a71..160cc58 100644
--- a/erpnext/projects/doctype/task/task.json
+++ b/erpnext/projects/doctype/task/task.json
@@ -12,6 +12,7 @@
   "issue",
   "type",
   "is_group",
+  "is_template",
   "column_break0",
   "status",
   "priority",
@@ -22,9 +23,11 @@
   "sb_timeline",
   "exp_start_date",
   "expected_time",
+  "start",
   "column_break_11",
   "exp_end_date",
   "progress",
+  "duration",
   "is_milestone",
   "sb_details",
   "description",
@@ -112,7 +115,7 @@
    "no_copy": 1,
    "oldfieldname": "status",
    "oldfieldtype": "Select",
-   "options": "Open\nWorking\nPending Review\nOverdue\nCompleted\nCancelled"
+   "options": "Open\nWorking\nPending Review\nOverdue\nTemplate\nCompleted\nCancelled"
   },
   {
    "fieldname": "priority",
@@ -360,6 +363,24 @@
    "label": "Completed By",
    "no_copy": 1,
    "options": "User"
+  },
+  {
+   "default": "0",
+   "fieldname": "is_template",
+   "fieldtype": "Check",
+   "label": "Is Template"
+  },
+  {
+   "depends_on": "is_template",
+   "fieldname": "start",
+   "fieldtype": "Int",
+   "label": "Begin On (Days)"
+  },
+  {
+   "depends_on": "is_template",
+   "fieldname": "duration",
+   "fieldtype": "Int",
+   "label": "Duration (Days)"
   }
  ],
  "icon": "fa fa-check",
@@ -367,7 +388,7 @@
  "is_tree": 1,
  "links": [],
  "max_attachments": 5,
- "modified": "2020-07-03 12:36:04.960457",
+ "modified": "2020-12-28 11:32:58.714991",
  "modified_by": "Administrator",
  "module": "Projects",
  "name": "Task",
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index fb84094..a996033 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -17,291 +17,305 @@
 class EndDateCannotBeGreaterThanProjectEndDateError(frappe.ValidationError): pass
 
 class Task(NestedSet):
-	nsm_parent_field = 'parent_task'
+    nsm_parent_field = 'parent_task'
 
-	def get_feed(self):
-		return '{0}: {1}'.format(_(self.status), self.subject)
+    def get_feed(self):
+        return '{0}: {1}'.format(_(self.status), self.subject)
 
-	def get_customer_details(self):
-		cust = frappe.db.sql("select customer_name from `tabCustomer` where name=%s", self.customer)
-		if cust:
-			ret = {'customer_name': cust and cust[0][0] or ''}
-			return ret
+    def get_customer_details(self):
+        cust = frappe.db.sql("select customer_name from `tabCustomer` where name=%s", self.customer)
+        if cust:
+            ret = {'customer_name': cust and cust[0][0] or ''}
+            return ret
 
-	def validate(self):
-		self.validate_dates()
-		self.validate_parent_project_dates()
-		self.validate_progress()
-		self.validate_status()
-		self.update_depends_on()
+    def validate(self):
+        self.validate_dates()
+        self.validate_parent_project_dates()
+        self.validate_progress()
+        self.validate_status()
+        self.update_depends_on()
+        self.validate_dependencies_for_template_task()
 
-	def validate_dates(self):
-		if self.exp_start_date and self.exp_end_date and getdate(self.exp_start_date) > getdate(self.exp_end_date):
-			frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Expected Start Date"), \
-				frappe.bold("Expected End Date")))
+    def validate_dates(self):
+        if self.exp_start_date and self.exp_end_date and getdate(self.exp_start_date) > getdate(self.exp_end_date):
+            frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Expected Start Date"), \
+                frappe.bold("Expected End Date")))
 
-		if self.act_start_date and self.act_end_date and getdate(self.act_start_date) > getdate(self.act_end_date):
-			frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Actual Start Date"), \
-				frappe.bold("Actual End Date")))
+        if self.act_start_date and self.act_end_date and getdate(self.act_start_date) > getdate(self.act_end_date):
+            frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Actual Start Date"), \
+                frappe.bold("Actual End Date")))
 
-	def validate_parent_project_dates(self):
-		if not self.project or frappe.flags.in_test:
-			return
+    def validate_parent_project_dates(self):
+        if not self.project or frappe.flags.in_test:
+            return
 
-		expected_end_date = frappe.db.get_value("Project", self.project, "expected_end_date")
+        expected_end_date = frappe.db.get_value("Project", self.project, "expected_end_date")
 
-		if expected_end_date:
-			validate_project_dates(getdate(expected_end_date), self, "exp_start_date", "exp_end_date", "Expected")
-			validate_project_dates(getdate(expected_end_date), self, "act_start_date", "act_end_date", "Actual")
+        if expected_end_date:
+            validate_project_dates(getdate(expected_end_date), self, "exp_start_date", "exp_end_date", "Expected")
+            validate_project_dates(getdate(expected_end_date), self, "act_start_date", "act_end_date", "Actual")
 
-	def validate_status(self):
-		if self.status!=self.get_db_value("status") and self.status == "Completed":
-			for d in self.depends_on:
-				if frappe.db.get_value("Task", d.task, "status") not in ("Completed", "Cancelled"):
-					frappe.throw(_("Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.").format(frappe.bold(self.name), frappe.bold(d.task)))
+    def validate_status(self):
+        if self.is_template and self.status != "Template":
+            self.status = "Template"
+        if self.status!=self.get_db_value("status") and self.status == "Completed":
+            for d in self.depends_on:
+                if frappe.db.get_value("Task", d.task, "status") not in ("Completed", "Cancelled"):
+                    frappe.throw(_("Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.").format(frappe.bold(self.name), frappe.bold(d.task)))
 
-			close_all_assignments(self.doctype, self.name)
+            close_all_assignments(self.doctype, self.name)
 
-	def validate_progress(self):
-		if flt(self.progress or 0) > 100:
-			frappe.throw(_("Progress % for a task cannot be more than 100."))
+    def validate_progress(self):
+        if flt(self.progress or 0) > 100:
+            frappe.throw(_("Progress % for a task cannot be more than 100."))
 
-		if flt(self.progress) == 100:
-			self.status = 'Completed'
+        if flt(self.progress) == 100:
+            self.status = 'Completed'
 
-		if self.status == 'Completed':
-			self.progress = 100
+        if self.status == 'Completed':
+            self.progress = 100
 
-	def update_depends_on(self):
-		depends_on_tasks = self.depends_on_tasks or ""
-		for d in self.depends_on:
-			if d.task and not d.task in depends_on_tasks:
-				depends_on_tasks += d.task + ","
-		self.depends_on_tasks = depends_on_tasks
+    def validate_dependencies_for_template_task(self):
+        if self.is_template:
+            self.validate_depends_on_tasks()
+                
+    def validate_depends_on_tasks(self):
+        if self.depends_on:
+            for task in self.depends_on:
+                if not frappe.db.get_value("Task", task.task, "is_template"):
+                    dependent_task_format = """<a href="#Form/Task/{0}">{0}</a>""".format(task.task)
+                    frappe.throw(_("Dependent Task {0} is not a Template Task").format(dependent_task_format))
 
-	def update_nsm_model(self):
-		frappe.utils.nestedset.update_nsm(self)
+    def update_depends_on(self):
+        depends_on_tasks = self.depends_on_tasks or ""
+        for d in self.depends_on:
+            if d.task and d.task not in depends_on_tasks:
+                depends_on_tasks += d.task + ","
+        self.depends_on_tasks = depends_on_tasks
 
-	def on_update(self):
-		self.update_nsm_model()
-		self.check_recursion()
-		self.reschedule_dependent_tasks()
-		self.update_project()
-		self.unassign_todo()
-		self.populate_depends_on()
+    def update_nsm_model(self):
+        frappe.utils.nestedset.update_nsm(self)
 
-	def unassign_todo(self):
-		if self.status == "Completed":
-			close_all_assignments(self.doctype, self.name)
-		if self.status == "Cancelled":
-			clear(self.doctype, self.name)
+    def on_update(self):
+        self.update_nsm_model()
+        self.check_recursion()
+        self.reschedule_dependent_tasks()
+        self.update_project()
+        self.unassign_todo()
+        self.populate_depends_on()
 
-	def update_total_expense_claim(self):
-		self.total_expense_claim = frappe.db.sql("""select sum(total_sanctioned_amount) from `tabExpense Claim`
-			where project = %s and task = %s and docstatus=1""",(self.project, self.name))[0][0]
+    def unassign_todo(self):
+        if self.status == "Completed":
+            close_all_assignments(self.doctype, self.name)
+        if self.status == "Cancelled":
+            clear(self.doctype, self.name)
 
-	def update_time_and_costing(self):
-		tl = frappe.db.sql("""select min(from_time) as start_date, max(to_time) as end_date,
-			sum(billing_amount) as total_billing_amount, sum(costing_amount) as total_costing_amount,
-			sum(hours) as time from `tabTimesheet Detail` where task = %s and docstatus=1"""
-			,self.name, as_dict=1)[0]
-		if self.status == "Open":
-			self.status = "Working"
-		self.total_costing_amount= tl.total_costing_amount
-		self.total_billing_amount= tl.total_billing_amount
-		self.actual_time= tl.time
-		self.act_start_date= tl.start_date
-		self.act_end_date= tl.end_date
+    def update_total_expense_claim(self):
+        self.total_expense_claim = frappe.db.sql("""select sum(total_sanctioned_amount) from `tabExpense Claim`
+            where project = %s and task = %s and docstatus=1""",(self.project, self.name))[0][0]
 
-	def update_project(self):
-		if self.project and not self.flags.from_project:
-			frappe.get_cached_doc("Project", self.project).update_project()
+    def update_time_and_costing(self):
+        tl = frappe.db.sql("""select min(from_time) as start_date, max(to_time) as end_date,
+            sum(billing_amount) as total_billing_amount, sum(costing_amount) as total_costing_amount,
+            sum(hours) as time from `tabTimesheet Detail` where task = %s and docstatus=1"""
+            ,self.name, as_dict=1)[0]
+        if self.status == "Open":
+            self.status = "Working"
+        self.total_costing_amount= tl.total_costing_amount
+        self.total_billing_amount= tl.total_billing_amount
+        self.actual_time= tl.time
+        self.act_start_date= tl.start_date
+        self.act_end_date= tl.end_date
 
-	def check_recursion(self):
-		if self.flags.ignore_recursion_check: return
-		check_list = [['task', 'parent'], ['parent', 'task']]
-		for d in check_list:
-			task_list, count = [self.name], 0
-			while (len(task_list) > count ):
-				tasks = frappe.db.sql(" select %s from `tabTask Depends On` where %s = %s " %
-					(d[0], d[1], '%s'), cstr(task_list[count]))
-				count = count + 1
-				for b in tasks:
-					if b[0] == self.name:
-						frappe.throw(_("Circular Reference Error"), CircularReferenceError)
-					if b[0]:
-						task_list.append(b[0])
+    def update_project(self):
+        if self.project and not self.flags.from_project:
+            frappe.get_cached_doc("Project", self.project).update_project()
 
-				if count == 15:
-					break
+    def check_recursion(self):
+        if self.flags.ignore_recursion_check: return
+        check_list = [['task', 'parent'], ['parent', 'task']]
+        for d in check_list:
+            task_list, count = [self.name], 0
+            while (len(task_list) > count ):
+                tasks = frappe.db.sql(" select %s from `tabTask Depends On` where %s = %s " %
+                    (d[0], d[1], '%s'), cstr(task_list[count]))
+                count = count + 1
+                for b in tasks:
+                    if b[0] == self.name:
+                        frappe.throw(_("Circular Reference Error"), CircularReferenceError)
+                    if b[0]:
+                        task_list.append(b[0])
 
-	def reschedule_dependent_tasks(self):
-		end_date = self.exp_end_date or self.act_end_date
-		if end_date:
-			for task_name in frappe.db.sql("""
-				select name from `tabTask` as parent
-				where parent.project = %(project)s
-					and parent.name in (
-						select parent from `tabTask Depends On` as child
-						where child.task = %(task)s and child.project = %(project)s)
-			""", {'project': self.project, 'task':self.name }, as_dict=1):
-				task = frappe.get_doc("Task", task_name.name)
-				if task.exp_start_date and task.exp_end_date and task.exp_start_date < getdate(end_date) and task.status == "Open":
-					task_duration = date_diff(task.exp_end_date, task.exp_start_date)
-					task.exp_start_date = add_days(end_date, 1)
-					task.exp_end_date = add_days(task.exp_start_date, task_duration)
-					task.flags.ignore_recursion_check = True
-					task.save()
+                if count == 15:
+                    break
 
-	def has_webform_permission(self):
-		project_user = frappe.db.get_value("Project User", {"parent": self.project, "user":frappe.session.user} , "user")
-		if project_user:
-			return True
+    def reschedule_dependent_tasks(self):
+        end_date = self.exp_end_date or self.act_end_date
+        if end_date:
+            for task_name in frappe.db.sql("""
+                select name from `tabTask` as parent
+                where parent.project = %(project)s
+                    and parent.name in (
+                        select parent from `tabTask Depends On` as child
+                        where child.task = %(task)s and child.project = %(project)s)
+            """, {'project': self.project, 'task':self.name }, as_dict=1):
+                task = frappe.get_doc("Task", task_name.name)
+                if task.exp_start_date and task.exp_end_date and task.exp_start_date < getdate(end_date) and task.status == "Open":
+                    task_duration = date_diff(task.exp_end_date, task.exp_start_date)
+                    task.exp_start_date = add_days(end_date, 1)
+                    task.exp_end_date = add_days(task.exp_start_date, task_duration)
+                    task.flags.ignore_recursion_check = True
+                    task.save()
 
-	def populate_depends_on(self):
-		if self.parent_task:
-			parent = frappe.get_doc('Task', self.parent_task)
-			if not self.name in [row.task for row in parent.depends_on]:
-				parent.append("depends_on", {
-					"doctype": "Task Depends On",
-					"task": self.name,
-					"subject": self.subject
-				})
-				parent.save()
+    def has_webform_permission(self):
+        project_user = frappe.db.get_value("Project User", {"parent": self.project, "user":frappe.session.user} , "user")
+        if project_user:
+            return True
 
-	def on_trash(self):
-		if check_if_child_exists(self.name):
-			throw(_("Child Task exists for this Task. You can not delete this Task."))
+    def populate_depends_on(self):
+        if self.parent_task:
+            parent = frappe.get_doc('Task', self.parent_task)
+            if self.name not in [row.task for row in parent.depends_on]:
+                parent.append("depends_on", {
+                    "doctype": "Task Depends On",
+                    "task": self.name,
+                    "subject": self.subject
+                })
+                parent.save()
 
-		self.update_nsm_model()
+    def on_trash(self):
+        if check_if_child_exists(self.name):
+            throw(_("Child Task exists for this Task. You can not delete this Task."))
 
-	def after_delete(self):
-		self.update_project()
+        self.update_nsm_model()
 
-	def update_status(self):
-		if self.status not in ('Cancelled', 'Completed') and self.exp_end_date:
-			from datetime import datetime
-			if self.exp_end_date < datetime.now().date():
-				self.db_set('status', 'Overdue', update_modified=False)
-				self.update_project()
+    def after_delete(self):
+        self.update_project()
+
+    def update_status(self):
+        if self.status not in ('Cancelled', 'Completed') and self.exp_end_date:
+            from datetime import datetime
+            if self.exp_end_date < datetime.now().date():
+                self.db_set('status', 'Overdue', update_modified=False)
+                self.update_project()
 
 @frappe.whitelist()
 def check_if_child_exists(name):
-	child_tasks = frappe.get_all("Task", filters={"parent_task": name})
-	child_tasks = [get_link_to_form("Task", task.name) for task in child_tasks]
-	return child_tasks
+    child_tasks = frappe.get_all("Task", filters={"parent_task": name})
+    child_tasks = [get_link_to_form("Task", task.name) for task in child_tasks]
+    return child_tasks
 
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def get_project(doctype, txt, searchfield, start, page_len, filters):
-	from erpnext.controllers.queries import get_match_cond
-	return frappe.db.sql(""" select name from `tabProject`
-			where %(key)s like %(txt)s
-				%(mcond)s
-			order by name
-			limit %(start)s, %(page_len)s""" % {
-				'key': searchfield,
-				'txt': frappe.db.escape('%' + txt + '%'),
-				'mcond':get_match_cond(doctype),
-				'start': start,
-				'page_len': page_len
-			})
+    from erpnext.controllers.queries import get_match_cond
+    return frappe.db.sql(""" select name from `tabProject`
+            where %(key)s like %(txt)s
+                %(mcond)s
+            order by name
+            limit %(start)s, %(page_len)s""" % {
+                'key': searchfield,
+                'txt': frappe.db.escape('%' + txt + '%'),
+                'mcond':get_match_cond(doctype),
+                'start': start,
+                'page_len': page_len
+            })
 
 
 @frappe.whitelist()
 def set_multiple_status(names, status):
-	names = json.loads(names)
-	for name in names:
-		task = frappe.get_doc("Task", name)
-		task.status = status
-		task.save()
+    names = json.loads(names)
+    for name in names:
+        task = frappe.get_doc("Task", name)
+        task.status = status
+        task.save()
 
 def set_tasks_as_overdue():
-	tasks = frappe.get_all("Task", filters={"status": ["not in", ["Cancelled", "Completed"]]}, fields=["name", "status", "review_date"])
-	for task in tasks:
-		if task.status == "Pending Review":
-			if getdate(task.review_date) > getdate(today()):
-				continue
-		frappe.get_doc("Task", task.name).update_status()
+    tasks = frappe.get_all("Task", filters={"status": ["not in", ["Cancelled", "Completed"]]}, fields=["name", "status", "review_date"])
+    for task in tasks:
+        if task.status == "Pending Review":
+            if getdate(task.review_date) > getdate(today()):
+                continue
+        frappe.get_doc("Task", task.name).update_status()
 
 
 @frappe.whitelist()
 def make_timesheet(source_name, target_doc=None, ignore_permissions=False):
-	def set_missing_values(source, target):
-		target.append("time_logs", {
-			"hours": source.actual_time,
-			"completed": source.status == "Completed",
-			"project": source.project,
-			"task": source.name
-		})
+    def set_missing_values(source, target):
+        target.append("time_logs", {
+            "hours": source.actual_time,
+            "completed": source.status == "Completed",
+            "project": source.project,
+            "task": source.name
+        })
 
-	doclist = get_mapped_doc("Task", source_name, {
-			"Task": {
-				"doctype": "Timesheet"
-			}
-		}, target_doc, postprocess=set_missing_values, ignore_permissions=ignore_permissions)
+    doclist = get_mapped_doc("Task", source_name, {
+            "Task": {
+                "doctype": "Timesheet"
+            }
+        }, target_doc, postprocess=set_missing_values, ignore_permissions=ignore_permissions)
 
-	return doclist
+    return doclist
 
 
 @frappe.whitelist()
 def get_children(doctype, parent, task=None, project=None, is_root=False):
 
-	filters = [['docstatus', '<', '2']]
+    filters = [['docstatus', '<', '2']]
 
-	if task:
-		filters.append(['parent_task', '=', task])
-	elif parent and not is_root:
-		# via expand child
-		filters.append(['parent_task', '=', parent])
-	else:
-		filters.append(['ifnull(`parent_task`, "")', '=', ''])
+    if task:
+        filters.append(['parent_task', '=', task])
+    elif parent and not is_root:
+        # via expand child
+        filters.append(['parent_task', '=', parent])
+    else:
+        filters.append(['ifnull(`parent_task`, "")', '=', ''])
 
-	if project:
-		filters.append(['project', '=', project])
+    if project:
+        filters.append(['project', '=', project])
 
-	tasks = frappe.get_list(doctype, fields=[
-		'name as value',
-		'subject as title',
-		'is_group as expandable'
-	], filters=filters, order_by='name')
+    tasks = frappe.get_list(doctype, fields=[
+        'name as value',
+        'subject as title',
+        'is_group as expandable'
+    ], filters=filters, order_by='name')
 
-	# return tasks
-	return tasks
+    # return tasks
+    return tasks
 
 @frappe.whitelist()
 def add_node():
-	from frappe.desk.treeview import make_tree_args
-	args = frappe.form_dict
-	args.update({
-		"name_field": "subject"
-	})
-	args = make_tree_args(**args)
+    from frappe.desk.treeview import make_tree_args
+    args = frappe.form_dict
+    args.update({
+        "name_field": "subject"
+    })
+    args = make_tree_args(**args)
 
-	if args.parent_task == 'All Tasks' or args.parent_task == args.project:
-		args.parent_task = None
+    if args.parent_task == 'All Tasks' or args.parent_task == args.project:
+        args.parent_task = None
 
-	frappe.get_doc(args).insert()
+    frappe.get_doc(args).insert()
 
 @frappe.whitelist()
 def add_multiple_tasks(data, parent):
-	data = json.loads(data)
-	new_doc = {'doctype': 'Task', 'parent_task': parent if parent!="All Tasks" else ""}
-	new_doc['project'] = frappe.db.get_value('Task', {"name": parent}, 'project') or ""
+    data = json.loads(data)
+    new_doc = {'doctype': 'Task', 'parent_task': parent if parent!="All Tasks" else ""}
+    new_doc['project'] = frappe.db.get_value('Task', {"name": parent}, 'project') or ""
 
-	for d in data:
-		if not d.get("subject"): continue
-		new_doc['subject'] = d.get("subject")
-		new_task = frappe.get_doc(new_doc)
-		new_task.insert()
+    for d in data:
+        if not d.get("subject"): continue
+        new_doc['subject'] = d.get("subject")
+        new_task = frappe.get_doc(new_doc)
+        new_task.insert()
 
 def on_doctype_update():
-	frappe.db.add_index("Task", ["lft", "rgt"])
+    frappe.db.add_index("Task", ["lft", "rgt"])
 
 def validate_project_dates(project_end_date, task, task_start, task_end, actual_or_expected_date):
-	if task.get(task_start) and date_diff(project_end_date, getdate(task.get(task_start))) < 0:
-		frappe.throw(_("Task's {0} Start Date cannot be after Project's End Date.").format(actual_or_expected_date))
+    if task.get(task_start) and date_diff(project_end_date, getdate(task.get(task_start))) < 0:
+        frappe.throw(_("Task's {0} Start Date cannot be after Project's End Date.").format(actual_or_expected_date))
 
-	if task.get(task_end) and date_diff(project_end_date, getdate(task.get(task_end))) < 0:
-		frappe.throw(_("Task's {0} End Date cannot be after Project's End Date.").format(actual_or_expected_date))
+    if task.get(task_end) and date_diff(project_end_date, getdate(task.get(task_end))) < 0:
+        frappe.throw(_("Task's {0} End Date cannot be after Project's End Date.").format(actual_or_expected_date))
diff --git a/erpnext/projects/doctype/task/task_list.js b/erpnext/projects/doctype/task/task_list.js
index 941fe97..39734ee 100644
--- a/erpnext/projects/doctype/task/task_list.js
+++ b/erpnext/projects/doctype/task/task_list.js
@@ -20,7 +20,8 @@
 			"Pending Review": "orange",
 			"Working": "orange",
 			"Completed": "green",
-			"Cancelled": "dark grey"
+			"Cancelled": "dark grey",
+			"Template": "blue"
 		}
 		return [__(doc.status), colors[doc.status], "status,=," + doc.status];
 	},
diff --git a/erpnext/projects/doctype/task/test_task.py b/erpnext/projects/doctype/task/test_task.py
index 47a28fd..0fad5e8 100644
--- a/erpnext/projects/doctype/task/test_task.py
+++ b/erpnext/projects/doctype/task/test_task.py
@@ -30,14 +30,16 @@
 		})
 
 	def test_reschedule_dependent_task(self):
+		project = frappe.get_value("Project", {"project_name": "_Test Project"})
+
 		task1 = create_task("_Test Task 1", nowdate(), add_days(nowdate(), 10))
 
 		task2 = create_task("_Test Task 2", add_days(nowdate(), 11), add_days(nowdate(), 15), task1.name)
-		task2.get("depends_on")[0].project = "_Test Project"
+		task2.get("depends_on")[0].project = project
 		task2.save()
 
 		task3 = create_task("_Test Task 3", add_days(nowdate(), 11), add_days(nowdate(), 15), task2.name)
-		task3.get("depends_on")[0].project = "_Test Project"
+		task3.get("depends_on")[0].project = project
 		task3.save()
 
 		task1.update({
@@ -97,14 +99,19 @@
 
 		self.assertEqual(frappe.db.get_value("Task", task.name, "status"), "Overdue")
 
-def create_task(subject, start=None, end=None, depends_on=None, project=None, save=True):
+def create_task(subject, start=None, end=None, depends_on=None, project=None, parent_task=None, is_group=0, is_template=0, begin=0, duration=0, save=True):
 	if not frappe.db.exists("Task", subject):
 		task = frappe.new_doc('Task')
 		task.status = "Open"
 		task.subject = subject
 		task.exp_start_date = start or nowdate()
 		task.exp_end_date = end or nowdate()
-		task.project = project or "_Test Project"
+		task.project = project or None if is_template else frappe.get_value("Project", {"project_name": "_Test Project"})
+		task.is_template = is_template
+		task.start = begin
+		task.duration = duration
+		task.is_group = is_group
+		task.parent_task = parent_task
 		if save:
 			task.save()
 	else:
@@ -116,5 +123,4 @@
 		})
 		if save:
 			task.save()
-
 	return task
diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py
index a5ce44d..4cb3804 100644
--- a/erpnext/projects/doctype/timesheet/test_timesheet.py
+++ b/erpnext/projects/doctype/timesheet/test_timesheet.py
@@ -89,10 +89,11 @@
 
 	def test_timesheet_billing_based_on_project(self):
 		emp = make_employee("test_employee_6@salary.com")
+		project = frappe.get_value("Project", {"project_name": "_Test Project"})
 
-		timesheet = make_timesheet(emp, simulate=True, billable=1, project = '_Test Project', company='_Test Company')
+		timesheet = make_timesheet(emp, simulate=True, billable=1, project=project, company='_Test Company')
 		sales_invoice = create_sales_invoice(do_not_save=True)
-		sales_invoice.project = '_Test Project'
+		sales_invoice.project = project
 		sales_invoice.submit()
 
 		ts = frappe.get_doc('Timesheet', timesheet.name)
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index b068245..b123af5 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -134,7 +134,7 @@
 		});
 	},
 
-	project: function(frm) {
+	parent_project: function(frm) {
 		set_project_in_timelog(frm);
 	},
 
@@ -168,8 +168,8 @@
 	},
 
 	time_logs_add: function(frm, cdt, cdn) {
-		if(frm.doc.project) {
-			frappe.model.set_value(cdt, cdn, 'project', frm.doc.project);
+		if(frm.doc.parent_project) {
+			frappe.model.set_value(cdt, cdn, 'project', frm.doc.parent_project);
 		}
 
 		var $trigger_again = $('.form-grid').find('.grid-row').find('.btn-open-row');
@@ -308,7 +308,9 @@
 };
 
 function set_project_in_timelog(frm) {
-	if(frm.doc.project){
-		erpnext.utils.copy_value_in_all_rows(frm.doc, frm.doc.doctype, frm.doc.name, "time_logs", "project");
+	if(frm.doc.parent_project) {
+		$.each(frm.doc.time_logs || [], function(i, item) {
+			frappe.model.set_value(item.doctype, item.name, "project", frm.doc.parent_project);
+		});
 	}
 }
\ No newline at end of file
diff --git a/erpnext/projects/doctype/timesheet/timesheet.json b/erpnext/projects/doctype/timesheet/timesheet.json
index 4c2edf4..b286821 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.json
+++ b/erpnext/projects/doctype/timesheet/timesheet.json
@@ -15,7 +15,7 @@
   "column_break_3",
   "salary_slip",
   "status",
-  "project",
+  "parent_project",
   "employee_detail",
   "employee",
   "employee_name",
@@ -261,7 +261,7 @@
    "read_only": 1
   },
   {
-   "fieldname": "project",
+   "fieldname": "parent_project",
    "fieldtype": "Link",
    "label": "Project",
    "options": "Project"
@@ -271,7 +271,7 @@
  "idx": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-10-29 07:50:35.938231",
+ "modified": "2021-01-08 20:51:14.590080",
  "modified_by": "Administrator",
  "module": "Projects",
  "name": "Timesheet",
diff --git a/erpnext/public/build.json b/erpnext/public/build.json
index 2f15cbc..f0212db 100644
--- a/erpnext/public/build.json
+++ b/erpnext/public/build.json
@@ -55,6 +55,8 @@
 	"js/item-dashboard.min.js": [
 		"stock/dashboard/item_dashboard.html",
 		"stock/dashboard/item_dashboard_list.html",
-		"stock/dashboard/item_dashboard.js"
+		"stock/dashboard/item_dashboard.js",
+		"stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html",
+		"stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html"
 	]
 }
diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js
index 29f3595..649eb45 100644
--- a/erpnext/public/js/controllers/accounts.js
+++ b/erpnext/public/js/controllers/accounts.js
@@ -31,15 +31,6 @@
 					}
 				}
 			});
-
-			frm.set_query("cost_center", "taxes", function(doc) {
-				return {
-					filters: {
-						'company': doc.company,
-						"is_group": 0
-					}
-				}
-			});
 		}
 	},
 	validate: function(frm) {
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index db85a3e..c963866 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -191,7 +191,10 @@
 			item.rejected_qty = flt(item.received_qty - item.qty, precision("rejected_qty", item));
 			item.received_stock_qty = flt(item.conversion_factor, precision("conversion_factor", item)) * flt(item.received_qty);
 		}
+		this._super(doc, cdt, cdn);
+	},
 
+	batch_no: function(doc, cdt, cdn) {
 		this._super(doc, cdt, cdn);
 	},
 
@@ -516,4 +519,4 @@
 	});
 
 	dialog.show();
-}
+}
\ No newline at end of file
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 3bc20f8..dce8e5d 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -1,6 +1,8 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
+frappe.provide('erpnext.accounts.dimensions');
+
 erpnext.TransactionController = erpnext.taxes_and_totals.extend({
 	setup: function() {
 		this._super();
@@ -38,7 +40,7 @@
 
 			cur_frm.cscript.set_gross_profit(item);
 			cur_frm.cscript.calculate_taxes_and_totals();
-
+			cur_frm.cscript.calculate_stock_uom_rate(frm, cdt, cdn);
 		});
 
 
@@ -103,9 +105,19 @@
 		frappe.ui.form.on(this.frm.doctype + " Item", {
 			items_add: function(frm, cdt, cdn) {
 				var item = frappe.get_doc(cdt, cdn);
-				if(!item.warehouse && frm.doc.set_warehouse) {
+				if (!item.warehouse && frm.doc.set_warehouse) {
 					item.warehouse = frm.doc.set_warehouse;
 				}
+
+				if (!item.target_warehouse && frm.doc.set_target_warehouse) {
+					item.target_warehouse = frm.doc.set_target_warehouse;
+				}
+
+				if (!item.from_warehouse && frm.doc.set_from_warehouse) {
+					item.from_warehouse = frm.doc.set_from_warehouse;
+				}
+
+				erpnext.accounts.dimensions.copy_dimension_from_first_row(frm, cdt, cdn, 'items');
 			}
 		});
 
@@ -159,16 +171,6 @@
 				};
 			});
 		}
-		if (this.frm.fields_dict["items"].grid.get_field("cost_center")) {
-			this.frm.set_query("cost_center", "items", function(doc) {
-				return {
-					filters: {
-						"company": doc.company,
-						"is_group": 0
-					}
-				};
-			});
-		}
 
 		if (this.frm.fields_dict["items"].grid.get_field("expense_account")) {
 			this.frm.set_query("expense_account", "items", function(doc) {
@@ -233,6 +235,8 @@
 				}
 			};
 
+			this.frm.trigger('set_default_internal_warehouse');
+
 			return frappe.run_serially([
 				() => set_value('currency', currency),
 				() => set_value('price_list_currency', currency),
@@ -543,6 +547,7 @@
 							company: me.frm.doc.company,
 							order_type: me.frm.doc.order_type,
 							is_pos: cint(me.frm.doc.is_pos),
+							is_return: cint(me.frm.doc.is_return),
 							is_subcontracted: me.frm.doc.is_subcontracted,
 							transaction_date: me.frm.doc.transaction_date || me.frm.doc.posting_date,
 							ignore_pricing_rule: me.frm.doc.ignore_pricing_rule,
@@ -594,12 +599,22 @@
 										return frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"])
 											.then((r) => {
 												if (r.message &&
-													(r.message.has_batch_no || r.message.has_serial_no)) {
+												(r.message.has_batch_no || r.message.has_serial_no)) {
 													frappe.flags.hide_serial_batch_dialog = false;
 												}
 											});
 								},
 								() => {
+									// check if batch serial selector is disabled or not
+									if (show_batch_dialog && !frappe.flags.hide_serial_batch_dialog)
+										return frappe.db.get_single_value('Stock Settings', 'disable_serial_no_and_batch_selector')
+											.then((value) => {
+												if (value) {
+													frappe.flags.hide_serial_batch_dialog = true;
+												}
+											});
+								},
+								() => {
 									if(show_batch_dialog && !frappe.flags.hide_serial_batch_dialog) {
 										var d = locals[cdt][cdn];
 										$.each(r.message, function(k, v) {
@@ -653,7 +668,7 @@
 				args: item_args
 			},
 			callback: function(r) {
-				frappe.model.set_value(item.doctype, item.name, 'rate', r.message);
+				frappe.model.set_value(item.doctype, item.name, 'rate', r.message * item.conversion_factor);
 			}
 		});
 	},
@@ -719,6 +734,31 @@
 		this.calculate_taxes_and_totals(false);
 	},
 
+	update_stock: function() {
+		this.frm.trigger('set_default_internal_warehouse');
+	},
+
+	set_default_internal_warehouse: function() {
+		let me = this;
+		if ((this.frm.doc.doctype === 'Sales Invoice' && me.frm.doc.update_stock)
+			|| this.frm.doc.doctype == 'Delivery Note') {
+			if (this.frm.doc.is_internal_customer && this.frm.doc.company === this.frm.doc.represents_company) {
+				frappe.db.get_value('Company', this.frm.doc.company, 'default_in_transit_warehouse', function(value) {
+					me.frm.set_value('set_target_warehouse', value.default_in_transit_warehouse);
+				});
+			}
+		}
+
+		if ((this.frm.doc.doctype === 'Purchase Invoice' && me.frm.doc.update_stock)
+			|| this.frm.doc.doctype == 'Purchase Receipt') {
+			if (this.frm.doc.is_internal_supplier && this.frm.doc.company === this.frm.doc.represents_company) {
+				frappe.db.get_value('Company', this.frm.doc.company, 'default_in_transit_warehouse', function(value) {
+					me.frm.set_value('set_from_warehouse', value.default_in_transit_warehouse);
+				});
+			}
+		}
+	},
+
 	company: function() {
 		var me = this;
 		var set_pricing = function() {
@@ -805,7 +845,7 @@
 			in_list(['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'], this.frm.doctype)) {
 			erpnext.utils.get_shipping_address(this.frm, function(){
 				set_party_account(set_pricing);
-			})
+			});
 
 			// Get default company billing address in Purchase Invoice, Order and Receipt
 			frappe.call({
@@ -1081,6 +1121,7 @@
 				}
 			});
 		}
+		me.calculate_stock_uom_rate(doc, cdt, cdn);
 	},
 
 	conversion_factor: function(doc, cdt, cdn, dont_fetch_price_list_rate) {
@@ -1101,9 +1142,15 @@
 				frappe.meta.has_field(doc.doctype, "price_list_currency")) {
 				this.apply_price_list(item, true);
 			}
+			this.calculate_stock_uom_rate(doc, cdt, cdn);
 		}
 	},
 
+	batch_no: function(doc, cdt, cdn) {
+		let item = frappe.get_doc(cdt, cdn);
+		this.apply_price_list(item, true);
+	},
+
 	toggle_conversion_factor: function(item) {
 		// toggle read only property for conversion factor field if the uom and stock uom are same
 		if(this.frm.get_field('items').grid.fields_map.conversion_factor) {
@@ -1116,9 +1163,15 @@
 	qty: function(doc, cdt, cdn) {
 		let item = frappe.get_doc(cdt, cdn);
 		this.conversion_factor(doc, cdt, cdn, true);
+		this.calculate_stock_uom_rate(doc, cdt, cdn);
 		this.apply_pricing_rule(item, true);
 	},
 
+	calculate_stock_uom_rate: function(doc, cdt, cdn) {
+		let item = frappe.get_doc(cdt, cdn);
+		item.stock_uom_rate = flt(item.rate)/flt(item.conversion_factor);	
+		refresh_field("stock_uom_rate", item.name, item.parentfield);
+	},
 	service_stop_date: function(frm, cdt, cdn) {
 		var child = locals[cdt][cdn];
 
@@ -1229,7 +1282,7 @@
 		this.frm.set_currency_labels(["base_rate", "base_net_rate", "base_price_list_rate", "base_amount", "base_net_amount"],
 			company_currency, "items");
 
-		this.frm.set_currency_labels(["rate", "net_rate", "price_list_rate", "amount", "net_amount"],
+		this.frm.set_currency_labels(["rate", "net_rate", "price_list_rate", "amount", "net_amount", "stock_uom_rate"],
 			this.frm.doc.currency, "items");
 
 		if(this.frm.fields_dict["operations"]) {
@@ -1408,6 +1461,7 @@
 					"pricing_rules": d.pricing_rules,
 					"warehouse": d.warehouse,
 					"serial_no": d.serial_no,
+					"batch_no": d.batch_no,
 					"price_list_rate": d.price_list_rate,
 					"conversion_factor": d.conversion_factor || 1.0
 				});
@@ -1830,7 +1884,6 @@
 			frappe.throw(__("Please enter Item Code to get batch no"));
 		} else if (doc.doctype == "Purchase Receipt" ||
 			(doc.doctype == "Purchase Invoice" && doc.update_stock)) {
-
 			return {
 				filters: {'item': item.item_code}
 			}
@@ -1856,9 +1909,8 @@
 	set_query_for_item_tax_template: function(doc, cdt, cdn) {
 		var item = frappe.get_doc(cdt, cdn);
 		if(!item.item_code) {
-			frappe.throw(__("Please enter Item Code to get item taxes"));
+			return doc.company ? {filters: {company: doc.company}} : {};
 		} else {
-
 			let filters = {
 				'item_code': item.item_code,
 				'valid_from': ["<=", doc.transaction_date || doc.bill_date || doc.posting_date],
@@ -1966,6 +2018,14 @@
 		this.autofill_warehouse(this.frm.doc.items, "warehouse", this.frm.doc.set_warehouse);
 	},
 
+	set_target_warehouse: function() {
+		this.autofill_warehouse(this.frm.doc.items, "target_warehouse", this.frm.doc.set_target_warehouse);
+	},
+
+	set_from_warehouse: function() {
+		this.autofill_warehouse(this.frm.doc.items, "from_warehouse", this.frm.doc.set_from_warehouse);
+	},
+
 	autofill_warehouse : function (child_table, warehouse_field, warehouse) {
 		if (warehouse && child_table && child_table.length) {
 			let doctype = child_table[0].doctype;
@@ -2030,3 +2090,35 @@
 		}, show_dialog);
 	});
 }
+
+erpnext.apply_putaway_rule = (frm, purpose=null) => {
+	if (!frm.doc.company) {
+		frappe.throw({message: __("Please select a Company first."), title: __("Mandatory")});
+	}
+	if (!frm.doc.items.length) return;
+
+	frappe.call({
+		method: "erpnext.stock.doctype.putaway_rule.putaway_rule.apply_putaway_rule",
+		args: {
+			doctype: frm.doctype,
+			items: frm.doc.items,
+			company: frm.doc.company,
+			sync: true,
+			purpose: purpose
+		},
+		callback: (result) => {
+			if (!result.exc && result.message) {
+				frm.clear_table("items");
+
+				let items =  result.message;
+				items.forEach((row) => {
+					delete row["name"]; // dont overwrite name from server side
+					let child = frm.add_child("items");
+					Object.assign(child, row);
+					frm.script_manager.trigger("qty", child.doctype, child.name);
+				});
+				frm.get_field("items").grid.refresh();
+			}
+		}
+	});
+};
diff --git a/erpnext/public/js/queries.js b/erpnext/public/js/queries.js
index 560a561..b635adc 100644
--- a/erpnext/public/js/queries.js
+++ b/erpnext/public/js/queries.js
@@ -115,7 +115,26 @@
 				["Warehouse", "is_group", "=",0]
 
 			]
-		}
+		};
+	},
+
+	get_filtered_dimensions: function(doc, child_fields, dimension, company) {
+		let account = '';
+
+		child_fields.forEach((field) => {
+			if (!account) {
+				account = doc[field];
+			}
+		});
+
+		return {
+			query: "erpnext.controllers.queries.get_filtered_dimensions",
+			filters: {
+				'dimension': dimension,
+				'account': account,
+				'company': company
+			}
+		};
 	}
 });
 
diff --git a/erpnext/public/js/telephony.js b/erpnext/public/js/telephony.js
index bd7f890..f9caade 100644
--- a/erpnext/public/js/telephony.js
+++ b/erpnext/public/js/telephony.js
@@ -20,4 +20,4 @@
 				});
 		}
 	}
-});
\ No newline at end of file
+});
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 891bbe5..c39609b 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -194,15 +194,21 @@
 	add_dimensions: function(report_name, index) {
 		let filters = frappe.query_reports[report_name].filters;
 
-		erpnext.dimension_filters.forEach((dimension) => {
-			let found = filters.some(el => el.fieldname === dimension['fieldname']);
+		frappe.call({
+			method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.get_dimensions",
+			callback: function(r) {
+				let accounting_dimensions = r.message[0];
+				accounting_dimensions.forEach((dimension) => {
+					let found = filters.some(el => el.fieldname === dimension['fieldname']);
 
-			if (!found) {
-				filters.splice(index, 0 ,{
-					"fieldname": dimension["fieldname"],
-					"label": __(dimension["label"]),
-					"fieldtype": "Link",
-					"options": dimension["document_type"]
+					if (!found) {
+						filters.splice(index, 0, {
+							"fieldname": dimension["fieldname"],
+							"label": __(dimension["label"]),
+							"fieldtype": "Link",
+							"options": dimension["document_type"]
+						});
+					}
 				});
 			}
 		});
diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js
index b6720c0..96e1817 100644
--- a/erpnext/public/js/utils/dimension_tree_filter.js
+++ b/erpnext/public/js/utils/dimension_tree_filter.js
@@ -1,54 +1,83 @@
-frappe.provide('frappe.ui.form');
+frappe.provide('erpnext.accounts');
 
-let default_dimensions = {};
+erpnext.accounts.dimensions = {
+	setup_dimension_filters(frm, doctype) {
+		this.accounting_dimensions = [];
+		this.default_dimensions = {};
+		this.fetch_custom_dimensions(frm, doctype);
+	},
 
-let doctypes_with_dimensions = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset",
-	"Expense Claim", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note", "Shipping Rule", "Loyalty Program",
-	"Fee Schedule", "Fee Structure", "Stock Reconciliation", "Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool",
-	"Subscription", "Purchase Order", "Journal Entry", "Material Request", "Purchase Receipt", "Landed Cost Item", "Asset"];
+	fetch_custom_dimensions(frm, doctype) {
+		let me = this;
+		frappe.call({
+			method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.get_dimensions",
+			args: {
+				'with_cost_center_and_project': true
+			},
+			callback: function(r) {
+				me.accounting_dimensions = r.message[0];
+				me.default_dimensions = r.message[1];
+				me.setup_filters(frm, doctype);
+			}
+		});
+	},
 
-let child_docs = ["Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account",
-	"Material Request Item", "Delivery Note Item", "Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction",
-	"Landed Cost Item", "Asset Value Adjustment", "Opening Invoice Creation Tool Item", "Subscription Plan"];
-
-frappe.call({
-	method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.get_dimension_filters",
-	callback: function(r) {
-		erpnext.dimension_filters = r.message[0];
-		default_dimensions = r.message[1];
-	}
-});
-
-doctypes_with_dimensions.forEach((doctype) => {
-	frappe.ui.form.on(doctype, {
-		onload: function(frm) {
-			erpnext.dimension_filters.forEach((dimension) => {
+	setup_filters(frm, doctype) {
+		if (this.accounting_dimensions) {
+			this.accounting_dimensions.forEach((dimension) => {
 				frappe.model.with_doctype(dimension['document_type'], () => {
-					if(frappe.meta.has_field(dimension['document_type'], 'is_group')) {
-						frm.set_query(dimension['fieldname'], {
-							"is_group": 0
-						});
-					}
+					let parent_fields = [];
+					frappe.meta.get_docfields(doctype).forEach((df) => {
+						if (df.fieldtype === 'Link' && df.options === 'Account') {
+							parent_fields.push(df.fieldname);
+						} else if (df.fieldtype === 'Table') {
+							this.setup_child_filters(frm, df.options, df.fieldname, dimension['fieldname']);
+						}
+
+						if (frappe.meta.has_field(doctype, dimension['fieldname'])) {
+							this.setup_account_filters(frm, dimension['fieldname'], parent_fields);
+						}
+					});
 				});
 			});
-		},
+		}
+	},
 
-		company: function(frm) {
-			if(frm.doc.company && (Object.keys(default_dimensions || {}).length > 0)
-				&& default_dimensions[frm.doc.company]) {
-				frm.trigger('update_dimension');
-			}
-		},
+	setup_child_filters(frm, doctype, parentfield, dimension) {
+		let fields = [];
 
-		update_dimension: function(frm) {
-			erpnext.dimension_filters.forEach((dimension) => {
-				if(frm.is_new()) {
-					if(frm.doc.company && Object.keys(default_dimensions || {}).length > 0
-						&& default_dimensions[frm.doc.company]) {
+		if (frappe.meta.has_field(doctype, dimension)) {
+			frappe.model.with_doctype(doctype, () => {
+				frappe.meta.get_docfields(doctype).forEach((df) => {
+					if (df.fieldtype === 'Link' && df.options === 'Account') {
+						fields.push(df.fieldname);
+					}
+				});
 
-						let default_dimension = default_dimensions[frm.doc.company][dimension['fieldname']];
+				frm.set_query(dimension, parentfield, function(doc, cdt, cdn) {
+					let row = locals[cdt][cdn];
+					return erpnext.queries.get_filtered_dimensions(row, fields, dimension, doc.company);
+				});
+			});
+		}
+	},
 
-						if(default_dimension) {
+	setup_account_filters(frm, dimension, fields) {
+		frm.set_query(dimension, function(doc) {
+			return erpnext.queries.get_filtered_dimensions(doc, fields, dimension, doc.company);
+		});
+	},
+
+	update_dimension(frm, doctype) {
+		if (this.accounting_dimensions) {
+			this.accounting_dimensions.forEach((dimension) => {
+				if (frm.is_new()) {
+					if (frm.doc.company && Object.keys(this.default_dimensions || {}).length > 0
+						&& this.default_dimensions[frm.doc.company]) {
+
+						let default_dimension = this.default_dimensions[frm.doc.company][dimension['fieldname']];
+
+						if (default_dimension) {
 							if (frappe.meta.has_field(doctype, dimension['fieldname'])) {
 								frm.set_value(dimension['fieldname'], default_dimension);
 							}
@@ -61,23 +90,14 @@
 				}
 			});
 		}
-	});
-});
+	},
 
-child_docs.forEach((doctype) => {
-	frappe.ui.form.on(doctype, {
-		items_add: function(frm, cdt, cdn) {
-			erpnext.dimension_filters.forEach((dimension) => {
-				var row = frappe.get_doc(cdt, cdn);
-				frm.script_manager.copy_from_first_row("items", row, [dimension['fieldname']]);
-			});
-		},
-
-		accounts_add: function(frm, cdt, cdn) {
-			erpnext.dimension_filters.forEach((dimension) => {
-				var row = frappe.get_doc(cdt, cdn);
-				frm.script_manager.copy_from_first_row("accounts", row, [dimension['fieldname']]);
+	copy_dimension_from_first_row(frm, cdt, cdn, fieldname) {
+		if (frappe.meta.has_field(frm.doctype, fieldname) && this.accounting_dimensions) {
+			this.accounting_dimensions.forEach((dimension) => {
+				let row = frappe.get_doc(cdt, cdn);
+				frm.script_manager.copy_from_first_row(fieldname, row, [dimension['fieldname']]);
 			});
 		}
-	});
-});
\ No newline at end of file
+	}
+};
\ No newline at end of file
diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js
index 770704e..808dd5a 100644
--- a/erpnext/public/js/utils/party.js
+++ b/erpnext/public/js/utils/party.js
@@ -276,6 +276,12 @@
 
 erpnext.utils.get_shipping_address = function(frm, callback){
 	if (frm.doc.company) {
+		if (!(frm.doc.inter_com_order_reference || frm.doc.internal_invoice_reference ||
+			frm.doc.internal_order_reference)) {
+			if (callback) {
+				return callback();
+			}
+		}
 		frappe.call({
 			method: "erpnext.accounts.custom.address.get_shipping_address",
 			args: {
diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.json b/erpnext/regional/doctype/datev_settings/datev_settings.json
index 713e8e3..f60de4c 100644
--- a/erpnext/regional/doctype/datev_settings/datev_settings.json
+++ b/erpnext/regional/doctype/datev_settings/datev_settings.json
@@ -7,13 +7,14 @@
  "engine": "InnoDB",
  "field_order": [
   "client",
-  "account_number_length",
-  "column_break_2",
   "client_number",
-  "section_break_4",
+  "column_break_2",
+  "consultant_number",
   "consultant",
+  "section_break_4",
+  "account_number_length",
   "column_break_6",
-  "consultant_number"
+  "temporary_against_account_number"
  ],
  "fields": [
   {
@@ -66,10 +67,17 @@
    "fieldtype": "Int",
    "label": "Account Number Length",
    "reqd": 1
+  },
+  {
+   "allow_in_quick_entry": 1,
+   "fieldname": "temporary_against_account_number",
+   "fieldtype": "Data",
+   "label": "Temporary Against Account Number",
+   "reqd": 1
   }
  ],
  "links": [],
- "modified": "2020-11-05 17:52:11.674329",
+ "modified": "2020-11-19 19:00:09.088816",
  "modified_by": "Administrator",
  "module": "Regional",
  "name": "DATEV Settings",
diff --git a/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.json b/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.json
index 5c1c79d..3034370 100644
--- a/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.json
+++ b/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.json
@@ -24,9 +24,8 @@
   },
   {
    "fieldname": "reference_invoice",
-   "fieldtype": "Link",
-   "label": "Reference Invoice",
-   "options": "Sales Invoice"
+   "fieldtype": "Data",
+   "label": "Reference Invoice"
   },
   {
    "fieldname": "headers",
@@ -64,7 +63,7 @@
  ],
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-12-24 21:09:38.882866",
+ "modified": "2021-01-13 12:06:57.253111",
  "modified_by": "Administrator",
  "module": "Regional",
  "name": "E Invoice Request Log",
diff --git a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json
index 4dcb22a..db8bda7 100644
--- a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json
+++ b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json
@@ -7,6 +7,7 @@
  "field_order": [
   "enable",
   "section_break_2",
+  "sandbox_mode",
   "credentials",
   "auth_token",
   "token_expiry"
@@ -41,12 +42,18 @@
    "label": "Credentials",
    "mandatory_depends_on": "enable",
    "options": "E Invoice User"
+  },
+  {
+   "default": "0",
+   "fieldname": "sandbox_mode",
+   "fieldtype": "Check",
+   "label": "Sandbox Mode"
   }
  ],
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2020-12-22 15:34:57.280044",
+ "modified": "2021-01-13 12:04:49.449199",
  "modified_by": "Administrator",
  "module": "Regional",
  "name": "E Invoice Settings",
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/regional/doctype/tax_exemption_80g_certificate/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/regional/doctype/tax_exemption_80g_certificate/__init__.py
diff --git a/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.js b/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.js
new file mode 100644
index 0000000..54cde9c
--- /dev/null
+++ b/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.js
@@ -0,0 +1,67 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Tax Exemption 80G Certificate', {
+	refresh: function(frm) {
+		if (frm.doc.donor) {
+			frm.set_query('donation', function() {
+				return {
+					filters: {
+						docstatus: 1,
+						donor: frm.doc.donor
+					}
+				};
+			});
+		}
+	},
+
+	recipient: function(frm) {
+		if (frm.doc.recipient === 'Donor') {
+			frm.set_value({
+				'member': '',
+				'member_name': '',
+				'member_email': '',
+				'member_pan_number': '',
+				'fiscal_year': '',
+				'total': 0,
+				'payments': []
+			});
+		} else {
+			frm.set_value({
+				'donor': '',
+				'donor_name': '',
+				'donor_email': '',
+				'donor_pan_number': '',
+				'donation': '',
+				'date_of_donation': '',
+				'amount': 0,
+				'mode_of_payment': '',
+				'razorpay_payment_id': ''
+			});
+		}
+	},
+
+	get_payments: function(frm) {
+		frm.call({
+			doc: frm.doc,
+			method: 'get_payments',
+			freeze: true
+		});
+	},
+
+	company: function(frm) {
+		if ((frm.doc.member || frm.doc.donor) && frm.doc.company) {
+			frm.call({
+				doc: frm.doc,
+				method: 'set_company_address',
+				freeze: true
+			});
+		}
+	},
+
+	donation: function(frm) {
+		if (frm.doc.recipient === 'Donor' && !frm.doc.donor) {
+			frappe.msgprint(__('Please select donor first'));
+		}
+	}
+});
diff --git a/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.json b/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.json
new file mode 100644
index 0000000..9eee722
--- /dev/null
+++ b/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.json
@@ -0,0 +1,297 @@
+{
+ "actions": [],
+ "autoname": "naming_series:",
+ "creation": "2021-02-15 12:37:21.577042",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "naming_series",
+  "recipient",
+  "member",
+  "member_name",
+  "member_email",
+  "member_pan_number",
+  "donor",
+  "donor_name",
+  "donor_email",
+  "donor_pan_number",
+  "column_break_4",
+  "date",
+  "fiscal_year",
+  "section_break_11",
+  "company",
+  "company_address",
+  "company_address_display",
+  "column_break_14",
+  "company_pan_number",
+  "company_80g_number",
+  "company_80g_wef",
+  "title",
+  "section_break_6",
+  "get_payments",
+  "payments",
+  "total",
+  "donation_details_section",
+  "donation",
+  "date_of_donation",
+  "amount",
+  "column_break_27",
+  "mode_of_payment",
+  "razorpay_payment_id"
+ ],
+ "fields": [
+  {
+   "fieldname": "recipient",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Certificate Recipient",
+   "options": "Member\nDonor",
+   "reqd": 1
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Member\";",
+   "fieldname": "member",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Member",
+   "mandatory_depends_on": "eval:doc.recipient === \"Member\";",
+   "options": "Member"
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Member\";",
+   "fetch_from": "member.member_name",
+   "fieldname": "member_name",
+   "fieldtype": "Data",
+   "label": "Member Name",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Donor\";",
+   "fieldname": "donor",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Donor",
+   "mandatory_depends_on": "eval:doc.recipient === \"Donor\";",
+   "options": "Donor"
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "date",
+   "fieldtype": "Date",
+   "label": "Date",
+   "reqd": 1
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Member\";",
+   "fieldname": "section_break_6",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "payments",
+   "fieldtype": "Table",
+   "label": "Payments",
+   "options": "Tax Exemption 80G Certificate Detail"
+  },
+  {
+   "fieldname": "total",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Total",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Member\";",
+   "fieldname": "fiscal_year",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Fiscal Year",
+   "options": "Fiscal Year"
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
+  {
+   "fieldname": "get_payments",
+   "fieldtype": "Button",
+   "label": "Get Memberships"
+  },
+  {
+   "fieldname": "naming_series",
+   "fieldtype": "Select",
+   "label": "Naming Series",
+   "options": "NPO-80G-.YYYY.-"
+  },
+  {
+   "fieldname": "section_break_11",
+   "fieldtype": "Section Break",
+   "label": "Company Details"
+  },
+  {
+   "fieldname": "company_address",
+   "fieldtype": "Link",
+   "label": "Company Address",
+   "options": "Address"
+  },
+  {
+   "fieldname": "column_break_14",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fetch_from": "company.pan_details",
+   "fieldname": "company_pan_number",
+   "fieldtype": "Data",
+   "label": "PAN Number",
+   "read_only": 1
+  },
+  {
+   "fieldname": "company_address_display",
+   "fieldtype": "Small Text",
+   "hidden": 1,
+   "label": "Company Address Display",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fetch_from": "company.company_80g_number",
+   "fieldname": "company_80g_number",
+   "fieldtype": "Data",
+   "label": "80G Number",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "company.with_effect_from",
+   "fieldname": "company_80g_wef",
+   "fieldtype": "Date",
+   "label": "80G With Effect From",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Donor\";",
+   "fieldname": "donation_details_section",
+   "fieldtype": "Section Break",
+   "label": "Donation Details"
+  },
+  {
+   "fieldname": "donation",
+   "fieldtype": "Link",
+   "label": "Donation",
+   "mandatory_depends_on": "eval:doc.recipient === \"Donor\";",
+   "options": "Donation"
+  },
+  {
+   "fetch_from": "donation.amount",
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "label": "Amount",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "donation.mode_of_payment",
+   "fieldname": "mode_of_payment",
+   "fieldtype": "Link",
+   "label": "Mode of Payment",
+   "options": "Mode of Payment",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "donation.razorpay_payment_id",
+   "fieldname": "razorpay_payment_id",
+   "fieldtype": "Data",
+   "label": "RazorPay Payment ID",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "donation.date",
+   "fieldname": "date_of_donation",
+   "fieldtype": "Date",
+   "label": "Date of Donation",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_27",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Donor\";",
+   "fetch_from": "donor.donor_name",
+   "fieldname": "donor_name",
+   "fieldtype": "Data",
+   "label": "Donor Name",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Donor\";",
+   "fetch_from": "donor.email",
+   "fieldname": "donor_email",
+   "fieldtype": "Data",
+   "label": "Email",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Member\";",
+   "fetch_from": "member.email_id",
+   "fieldname": "member_email",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Email",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Member\";",
+   "fetch_from": "member.pan_number",
+   "fieldname": "member_pan_number",
+   "fieldtype": "Data",
+   "label": "PAN Details",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Donor\";",
+   "fetch_from": "donor.pan_number",
+   "fieldname": "donor_pan_number",
+   "fieldtype": "Data",
+   "label": "PAN Details",
+   "read_only": 1
+  },
+  {
+   "fieldname": "title",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Title",
+   "print_hide": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-02-22 00:03:34.215633",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "Tax Exemption 80G Certificate",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "search_fields": "member, member_name",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "title",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.py b/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.py
new file mode 100644
index 0000000..d734a18
--- /dev/null
+++ b/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.py
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from frappe.model.document import Document
+from frappe.utils import getdate, flt, get_link_to_form
+from erpnext.accounts.utils import get_fiscal_year
+from frappe.contacts.doctype.address.address import get_company_address
+
+class TaxExemption80GCertificate(Document):
+	def validate(self):
+		self.validate_date()
+		self.validate_duplicates()
+		self.validate_company_details()
+		self.set_company_address()
+		self.set_title()
+
+	def validate_date(self):
+		if self.recipient == 'Member':
+			if getdate(self.date):
+				fiscal_year = get_fiscal_year(fiscal_year=self.fiscal_year, as_dict=True)
+
+				if not (fiscal_year.year_start_date <= getdate(self.date) \
+					<= fiscal_year.year_end_date):
+					frappe.throw(_('The Certificate Date is not in the Fiscal Year {0}').format(frappe.bold(self.fiscal_year)))
+
+	def validate_duplicates(self):
+		if self.recipient == 'Donor':
+			certificate = frappe.db.exists(self.doctype, {'donation': self.donation})
+			if certificate:
+				frappe.throw(_('An 80G Certificate {0} already exists for the donation {1}').format(
+					get_link_to_form(self.doctype, certificate), frappe.bold(self.donation)
+				), title=_('Duplicate Certificate'))
+
+	def validate_company_details(self):
+		fields = ['company_80g_number', 'with_effect_from', 'pan_details']
+		company_details = frappe.db.get_value('Company', self.company, fields, as_dict=True)
+		if not company_details.company_80g_number:
+			frappe.throw(_('Please set the {0} for company {1}').format(frappe.bold('80G Number'),
+				get_link_to_form('Company', self.company)))
+
+		if not company_details.pan_details:
+			frappe.throw(_('Please set the {0} for company {1}').format(frappe.bold('PAN Number'),
+				get_link_to_form('Company', self.company)))
+
+	def set_company_address(self):
+		address = get_company_address(self.company)
+		self.company_address = address.company_address
+		self.company_address_display = address.company_address_display
+
+	def set_title(self):
+		if self.recipient == "Member":
+			self.title = self.member_name
+		else:
+			self.title = self.donor_name
+
+	def get_payments(self):
+		if not self.member:
+			frappe.throw(_('Please select a Member first.'))
+
+		fiscal_year = get_fiscal_year(fiscal_year=self.fiscal_year, as_dict=True)
+
+		memberships = frappe.db.get_all('Membership', {
+			'member': self.member,
+			'from_date': ['between', (fiscal_year.year_start_date, fiscal_year.year_end_date)],
+			'to_date': ['between', (fiscal_year.year_start_date, fiscal_year.year_end_date)],
+			'membership_status': ('!=', 'Cancelled')
+		}, ['from_date', 'amount', 'name', 'invoice', 'payment_id'])
+
+		if not memberships:
+			frappe.msgprint(_('No Membership Payments found against the Member {0}').format(self.member))
+
+		total = 0
+		self.payments = []
+
+		for doc in memberships:
+			self.append('payments', {
+				'date': doc.from_date,
+				'amount': doc.amount,
+				'invoice_id': doc.invoice,
+				'razorpay_payment_id': doc.payment_id,
+				'membership': doc.name
+			})
+			total += flt(doc.amount)
+
+		self.total = total
diff --git a/erpnext/regional/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py b/erpnext/regional/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py
new file mode 100644
index 0000000..346ebbf
--- /dev/null
+++ b/erpnext/regional/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+from frappe.utils import getdate
+from erpnext.accounts.utils import get_fiscal_year
+from erpnext.non_profit.doctype.donation.test_donation import create_donor, create_mode_of_payment, create_donor_type
+from erpnext.non_profit.doctype.donation.donation import create_donation
+from erpnext.non_profit.doctype.membership.test_membership import setup_membership, make_membership
+from erpnext.non_profit.doctype.member.member import create_member
+
+class TestTaxExemption80GCertificate(unittest.TestCase):
+	def setUp(self):
+		frappe.db.sql('delete from `tabTax Exemption 80G Certificate`')
+		frappe.db.sql('delete from `tabMembership`')
+		create_donor_type()
+		settings = frappe.get_doc('Non Profit Settings')
+		settings.company = '_Test Company'
+		settings.donation_company = '_Test Company'
+		settings.default_donor_type = '_Test Donor'
+		settings.creation_user = 'Administrator'
+		settings.save()
+
+		company = frappe.get_doc('Company', '_Test Company')
+		company.pan_details = 'BBBTI3374C'
+		company.company_80g_number = 'NQ.CIT(E)I2018-19/DEL-IE28615-27062018/10087'
+		company.with_effect_from = getdate()
+		company.save()
+
+	def test_duplicate_donation_certificate(self):
+		donor = create_donor()
+		create_mode_of_payment()
+		payment = frappe._dict({
+			'amount': 100,
+			'method': 'Debit Card',
+			'id': 'pay_MeXAmsgeKOhq7O'
+		})
+		donation = create_donation(donor, payment)
+
+		args = frappe._dict({
+			'recipient': 'Donor',
+			'donor': donor.name,
+			'donation': donation.name
+		})
+		certificate = create_80g_certificate(args)
+		certificate.insert()
+
+		# check company details
+		self.assertEquals(certificate.company_pan_number, 'BBBTI3374C')
+		self.assertEquals(certificate.company_80g_number, 'NQ.CIT(E)I2018-19/DEL-IE28615-27062018/10087')
+
+		# check donation details
+		self.assertEquals(certificate.amount, donation.amount)
+
+		duplicate_certificate = create_80g_certificate(args)
+		# duplicate validation
+		self.assertRaises(frappe.ValidationError, duplicate_certificate.insert)
+
+	def test_membership_80g_certificate(self):
+		plan = setup_membership()
+
+		# make test member
+		member_doc = create_member(frappe._dict({
+			'fullname': "_Test_Member",
+			'email': "_test_member_erpnext@example.com",
+			'plan_id': plan.name
+		}))
+		member_doc.make_customer_and_link()
+		member = member_doc.name
+
+		membership = make_membership(member, { "from_date": getdate() })
+		invoice = membership.generate_invoice(save=True)
+
+		args = frappe._dict({
+			'recipient': 'Member',
+			'member': member,
+			'fiscal_year': get_fiscal_year(getdate(), as_dict=True).get('name')
+		})
+		certificate = create_80g_certificate(args)
+		certificate.get_payments()
+		certificate.insert()
+
+		self.assertEquals(len(certificate.payments), 1)
+		self.assertEquals(certificate.payments[0].amount, membership.amount)
+		self.assertEquals(certificate.payments[0].invoice_id, invoice.name)
+
+
+def create_80g_certificate(args):
+	certificate = frappe.get_doc({
+		'doctype': 'Tax Exemption 80G Certificate',
+		'recipient': args.recipient,
+		'date': getdate(),
+		'company': '_Test Company'
+	})
+
+	certificate.update(args)
+
+	return certificate
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/regional/doctype/tax_exemption_80g_certificate_detail/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/regional/doctype/tax_exemption_80g_certificate_detail/__init__.py
diff --git a/erpnext/regional/doctype/tax_exemption_80g_certificate_detail/tax_exemption_80g_certificate_detail.json b/erpnext/regional/doctype/tax_exemption_80g_certificate_detail/tax_exemption_80g_certificate_detail.json
new file mode 100644
index 0000000..dfa817d
--- /dev/null
+++ b/erpnext/regional/doctype/tax_exemption_80g_certificate_detail/tax_exemption_80g_certificate_detail.json
@@ -0,0 +1,66 @@
+{
+ "actions": [],
+ "creation": "2021-02-15 12:43:52.754124",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "date",
+  "amount",
+  "invoice_id",
+  "column_break_4",
+  "razorpay_payment_id",
+  "membership"
+ ],
+ "fields": [
+  {
+   "fieldname": "date",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "label": "Date",
+   "reqd": 1
+  },
+  {
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Amount",
+   "reqd": 1
+  },
+  {
+   "fieldname": "invoice_id",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Invoice ID",
+   "options": "Sales Invoice",
+   "reqd": 1
+  },
+  {
+   "fieldname": "razorpay_payment_id",
+   "fieldtype": "Data",
+   "label": "Razorpay Payment ID"
+  },
+  {
+   "fieldname": "membership",
+   "fieldtype": "Link",
+   "label": "Membership",
+   "options": "Membership"
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-02-15 16:35:10.777587",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "Tax Exemption 80G Certificate Detail",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/tax_exemption_80g_certificate_detail/tax_exemption_80g_certificate_detail.py b/erpnext/regional/doctype/tax_exemption_80g_certificate_detail/tax_exemption_80g_certificate_detail.py
new file mode 100644
index 0000000..bdad798
--- /dev/null
+++ b/erpnext/regional/doctype/tax_exemption_80g_certificate_detail/tax_exemption_80g_certificate_detail.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class TaxExemption80GCertificateDetail(Document):
+	pass
diff --git a/erpnext/regional/germany/accounts_controller.py b/erpnext/regional/germany/accounts_controller.py
deleted file mode 100644
index 5b2b31f..0000000
--- a/erpnext/regional/germany/accounts_controller.py
+++ /dev/null
@@ -1,56 +0,0 @@
-import frappe
-from frappe import _
-from frappe import msgprint
-
-
-REQUIRED_FIELDS = {
-	"Sales Invoice": [
-		{
-			"field_name": "company_address",
-			"regulation": "§ 14 Abs. 4 Nr. 1 UStG"
-		},
-		{
-			"field_name": "company_tax_id",
-			"regulation": "§ 14 Abs. 4 Nr. 2 UStG"
-		},
-		{
-			"field_name": "taxes",
-			"regulation": "§ 14 Abs. 4 Nr. 8 UStG"
-		},
-		{
-			"field_name": "customer_address",
-			"regulation": "§ 14 Abs. 4 Nr. 1 UStG",
-			"condition": "base_grand_total > 250"
-		}
-	]
-}
-
-
-def validate_regional(doc):
-	"""Check if required fields for this document are present."""
-	required_fields = REQUIRED_FIELDS.get(doc.doctype)
-	if not required_fields:
-		return
-
-	meta = frappe.get_meta(doc.doctype)
-	field_map = {field.fieldname: field.label for field in meta.fields}
-
-	for field in required_fields:
-		condition = field.get("condition")
-		if condition and not frappe.safe_eval(condition, doc.as_dict()):
-			continue
-
-		field_name = field.get("field_name")
-		regulation = field.get("regulation")
-		if field_name and not doc.get(field_name):
-			missing(field_map.get(field_name), regulation)
-
-
-def missing(field_label, regulation):
-	"""Notify the user that a required field is missing."""
-	context = 'Specific for Germany. Example: Remember to set Company Tax ID. It is required by § 14 Abs. 4 Nr. 2 UStG.'
-	msgprint(_('Remember to set {field_label}. It is required by {regulation}.', context=context).format(
-			field_label=frappe.bold(_(field_label)),
-			regulation=regulation
-		)
-	)
diff --git a/erpnext/regional/india/__init__.py b/erpnext/regional/india/__init__.py
index d6221a8..378b735 100644
--- a/erpnext/regional/india/__init__.py
+++ b/erpnext/regional/india/__init__.py
@@ -20,6 +20,7 @@
  'Jharkhand',
  'Karnataka',
  'Kerala',
+ 'Ladakh',
  'Lakshadweep Islands',
  'Madhya Pradesh',
  'Maharashtra',
@@ -59,6 +60,7 @@
  "Jharkhand": "20",
  "Karnataka": "29",
  "Kerala": "32",
+ "Ladakh": "38",
  "Lakshadweep Islands": "31",
  "Madhya Pradesh": "23",
  "Maharashtra": "27",
@@ -80,4 +82,4 @@
  "West Bengal": "19",
 }
 
-number_state_mapping = {v: k for k, v in iteritems(state_numbers)}
\ No newline at end of file
+number_state_mapping = {v: k for k, v in iteritems(state_numbers)}
diff --git a/erpnext/regional/india/e_invoice/einv_template.json b/erpnext/regional/india/e_invoice/einv_template.json
index e5751da..60f490d 100644
--- a/erpnext/regional/india/e_invoice/einv_template.json
+++ b/erpnext/regional/india/e_invoice/einv_template.json
@@ -59,7 +59,7 @@
         {item_list}
     ],
     "ValDtls": {{
-        "AssVal": "{invoice_value_details.base_net_total}",
+        "AssVal": "{invoice_value_details.base_total}",
         "CgstVal": "{invoice_value_details.total_cgst_amt}",
         "SgstVal": "{invoice_value_details.total_sgst_amt}",
         "IgstVal": "{invoice_value_details.total_igst_amt}",
diff --git a/erpnext/regional/india/e_invoice/einvoice.js b/erpnext/regional/india/e_invoice/einvoice.js
index 9c86cc8..e8a7c30 100644
--- a/erpnext/regional/india/e_invoice/einvoice.js
+++ b/erpnext/regional/india/e_invoice/einvoice.js
@@ -18,6 +18,9 @@
 
 			if (!irn && !__unsaved) {
 				const action = () => {
+					if (frm.doc.__unsaved) {
+						frappe.throw(__('Please save the document to generate IRN.'));
+					}
 					frappe.call({
 						method: 'erpnext.regional.india.e_invoice.utils.get_einvoice',
 						args: { doctype, docname: name },
@@ -42,7 +45,7 @@
 						"default": "1-Duplicate",
 						"options": ["1-Duplicate", "2-Data Entry Error", "3-Order Cancelled", "4-Other"]
 					},
-					{ 
+					{
 						"label": "Remark",
 						"fieldname": "remark",
 						"fieldtype": "Data",
@@ -57,7 +60,7 @@
 							const data = d.get_values();
 							frappe.call({
 								method: 'erpnext.regional.india.e_invoice.utils.cancel_irn',
-								args: { 
+								args: {
 									doctype,
 									docname: name,
 									irn: irn,
@@ -185,7 +188,6 @@
 			'fieldname': 'vehicle_no',
 			'label': 'Vehicle No',
 			'fieldtype': 'Data',
-			'depends_on': 'eval:(doc.mode_of_transport === "Road")',
 			'default': frm.doc.vehicle_no
 		},
 		{
@@ -252,7 +254,7 @@
 		title: __("Preview"),
 		wide: 1,
 		fields: [
-			{ 
+			{
 				"label": "Preview",
 				"fieldname": "preview_html",
 				"fieldtype": "HTML"
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
index cb92c42..438ec79 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -11,19 +11,22 @@
 import base64
 import frappe
 import traceback
+import io
 from frappe import _, bold
 from pyqrcode import create as qrcreate
 from frappe.integrations.utils import make_post_request, make_get_request
 from erpnext.regional.india.utils import get_gst_accounts, get_place_of_supply
-from frappe.utils.data import cstr, cint, format_date, flt, time_diff_in_seconds, now_datetime, add_to_date
+from frappe.utils.data import cstr, cint, format_date, flt, time_diff_in_seconds, now_datetime, add_to_date, get_link_to_form
 
 def validate_einvoice_fields(doc):
 	einvoicing_enabled = cint(frappe.db.get_value('E Invoice Settings', 'E Invoice Settings', 'enable'))
-	invalid_doctype = doc.doctype not in ['Sales Invoice']
+	invalid_doctype = doc.doctype != 'Sales Invoice'
 	invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export']
 	company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin')
-
-	if not einvoicing_enabled or invalid_doctype or invalid_supply_type or company_transaction: return
+	no_taxes_applied = not doc.get('taxes')
+	
+	if not einvoicing_enabled or invalid_doctype or invalid_supply_type or company_transaction or no_taxes_applied:
+		return
 
 	if doc.docstatus == 0 and doc._action == 'save':
 		if doc.irn:
@@ -34,7 +37,7 @@
 	elif doc.docstatus == 1 and doc._action == 'submit' and not doc.irn:
 		frappe.throw(_('You must generate IRN before submitting the document.'), title=_('Missing IRN'))
 
-	elif doc.docstatus == 2 and doc._action == 'cancel' and not doc.irn_cancelled:
+	elif doc.irn and doc.docstatus == 2 and doc._action == 'cancel' and not doc.irn_cancelled:
 		frappe.throw(_('You must cancel IRN before cancelling the document.'), title=_('Cancel Not Allowed'))
 
 def raise_document_name_too_long_error():
@@ -60,7 +63,7 @@
 	elif invoice.gst_category == 'Overseas': supply_type = 'EXPWOP'
 	elif invoice.gst_category == 'Deemed Export': supply_type = 'DEXP'
 
-	if not supply_type: 
+	if not supply_type:
 		rr, sez, overseas, export = bold('Registered Regular'), bold('SEZ'), bold('Overseas'), bold('Deemed Export')
 		frappe.throw(_('GST category should be one of {}, {}, {}, {}').format(rr, sez, overseas, export),
 			title=_('Invalid Supply Type'))
@@ -84,29 +87,32 @@
 	))
 
 def get_party_details(address_name):
-	address = frappe.get_all('Address', filters={'name': address_name}, fields=['*'])[0]
-	gstin = address.get('gstin')
+	d = frappe.get_all('Address', filters={'name': address_name}, fields=['*'])[0]
 
-	gstin_details = get_gstin_details(gstin)
-	legal_name = gstin_details.get('LegalName')
-	location = gstin_details.get('AddrLoc') or address.get('city')
-	state_code = gstin_details.get('StateCode')
-	pincode = gstin_details.get('AddrPncd')
-	address_line1 = '{} {}'.format(gstin_details.get('AddrBno'), gstin_details.get('AddrFlno'))
-	address_line2 = '{} {}'.format(gstin_details.get('AddrBnm'), gstin_details.get('AddrSt'))
-	email_id = address.get('email_id')
-	phone = address.get('phone')
-	# get last 10 digit 
-	phone = phone.replace(" ", "")[-10:] if phone else ''
+	if (not d.gstin
+		or not d.city
+		or not d.pincode
+		or not d.address_title
+		or not d.address_line1
+		or not d.gst_state_number):
 
-	if state_code == 97:
+		frappe.throw(
+			msg=_('Address lines, city, pincode, gstin is mandatory for address {}. Please set them and try again.').format(
+				get_link_to_form('Address', address_name)
+			),
+			title=_('Missing Address Fields')
+		)
+
+	if d.gst_state_number == 97:
 		# according to einvoice standard
 		pincode = 999999
 
 	return frappe._dict(dict(
-		gstin=gstin, legal_name=legal_name, location=location,
-		pincode=pincode, state_code=state_code, address_line1=address_line1,
-		address_line2=address_line2, email=email_id, phone=phone
+		gstin=d.gstin, legal_name=d.address_title,
+		location=d.city, pincode=d.pincode,
+		state_code=d.gst_state_number,
+		address_line1=d.address_line1,
+		address_line2=d.address_line2
 	))
 
 def get_gstin_details(gstin):
@@ -122,19 +128,27 @@
 	if details:
 		frappe.local.gstin_cache[key] = details
 		return details
-	
+
 	if not details:
 		return GSPConnector.get_gstin_details(gstin)
 
 def get_overseas_address_details(address_name):
-	address_title, address_line1, address_line2, city, phone, email_id = frappe.db.get_value(
-		'Address', address_name, ['address_title', 'address_line1', 'address_line2', 'city', 'phone', 'email_id']
+	address_title, address_line1, address_line2, city = frappe.db.get_value(
+		'Address', address_name, ['address_title', 'address_line1', 'address_line2', 'city']
 	)
 
+	if not address_title or not address_line1 or not city:
+		frappe.throw(
+			msg=_('Address lines and city is mandatory for address {}. Please set them and try again.').format(
+				get_link_to_form('Address', address_name)
+			),
+			title=_('Missing Address Fields')
+		)
+
 	return frappe._dict(dict(
-		gstin='URP', legal_name=address_title, address_line1=address_line1,
-		address_line2=address_line2, email=email_id, phone=phone,
-		pincode=999999, state_code=96, place_of_supply=96, location=city
+		gstin='URP', legal_name=address_title, location=city,
+		address_line1=address_line1, address_line2=address_line2,
+		pincode=999999, state_code=96, place_of_supply=96
 	))
 
 def get_item_list(invoice):
@@ -146,19 +160,21 @@
 		item.update(d.as_dict())
 
 		item.sr_no = d.idx
+		item.description = json.dumps(d.item_name)[1:-1]
+
 		item.qty = abs(item.qty)
-		item.description = d.item_name
+		item.discount_amount = 0
+		item.unit_rate = abs(item.base_net_amount / item.qty)
+		item.gross_amount = abs(item.base_net_amount)
 		item.taxable_value = abs(item.base_net_amount)
-		item.discount_amount = abs(item.discount_amount * item.qty)
-		item.unit_rate = abs(item.base_price_list_rate) if item.discount_amount else abs(item.base_net_rate)
-		item.gross_amount = abs(item.unit_rate * item.qty)
 
 		item.batch_expiry_date = frappe.db.get_value('Batch', d.batch_no, 'expiry_date') if d.batch_no else None
 		item.batch_expiry_date = format_date(item.batch_expiry_date, 'dd/mm/yyyy') if item.batch_expiry_date else None
 		item.is_service_item = 'N' if frappe.db.get_value('Item', d.item_code, 'is_stock_item') else 'Y'
+		item.serial_no = ""
 
 		item = update_item_taxes(invoice, item)
-		
+
 		item.total_value = abs(
 			item.taxable_value + item.igst_amount + item.sgst_amount +
 			item.cgst_amount + item.cess_amount + item.cess_nadv_amount + item.other_charges
@@ -180,38 +196,45 @@
 		item[attr] = 0
 
 	for t in invoice.taxes:
+		# this contains item wise tax rate & tax amount (incl. discount)
 		item_tax_detail = json.loads(t.item_wise_tax_detail).get(item.item_code)
 		if t.account_head in gst_accounts_list:
+			item_tax_rate = item_tax_detail[0]
+			# item tax amount excluding discount amount
+			item_tax_amount = (item_tax_rate / 100) * item.base_net_amount
+
 			if t.account_head in gst_accounts.cess_account:
+				item_tax_amount_after_discount = item_tax_detail[1]
 				if t.charge_type == 'On Item Quantity':
-					item.cess_nadv_amount += abs(item_tax_detail[1])
+					item.cess_nadv_amount += abs(item_tax_amount_after_discount)
 				else:
-					item.cess_rate += item_tax_detail[0]
-					item.cess_amount += abs(item_tax_detail[1])
-			elif t.account_head in gst_accounts.igst_account:
-				item.tax_rate += item_tax_detail[0]
-				item.igst_amount += abs(item_tax_detail[1])
-			elif t.account_head in gst_accounts.sgst_account:
-				item.tax_rate += item_tax_detail[0]
-				item.sgst_amount += abs(item_tax_detail[1])
-			elif t.account_head in gst_accounts.cgst_account:
-				item.tax_rate += item_tax_detail[0]
-				item.cgst_amount += abs(item_tax_detail[1])
-	
+					item.cess_rate += item_tax_rate
+					item.cess_amount += abs(item_tax_amount_after_discount)
+
+			for tax_type in ['igst', 'cgst', 'sgst']:
+				if t.account_head in gst_accounts[f'{tax_type}_account']:
+					item.tax_rate += item_tax_rate
+					item[f'{tax_type}_amount'] += abs(item_tax_amount)
+
 	return item
 
 def get_invoice_value_details(invoice):
 	invoice_value_details = frappe._dict(dict())
-	invoice_value_details.base_net_total = abs(invoice.base_net_total)
-	invoice_value_details.invoice_discount_amt = invoice.discount_amount if invoice.discount_amount and invoice.discount_amount > 0 else 0
-	# discount amount cannnot be -ve in an e-invoice, so if -ve include discount in round_off
-	invoice_value_details.round_off = invoice.rounding_adjustment - (invoice.discount_amount if invoice.discount_amount and invoice.discount_amount < 0 else 0)
-	disable_rounded = frappe.db.get_single_value('Global Defaults', 'disable_rounded_total')
-	invoice_value_details.base_grand_total = abs(invoice.base_grand_total) if disable_rounded else abs(invoice.base_rounded_total)
-	invoice_value_details.grand_total = abs(invoice.grand_total) if disable_rounded else abs(invoice.rounded_total)
-	
+
+	if invoice.apply_discount_on == 'Net Total' and invoice.discount_amount:
+		invoice_value_details.base_total = abs(invoice.base_total)
+		invoice_value_details.invoice_discount_amt = invoice.base_discount_amount
+	else:
+		invoice_value_details.base_total = abs(invoice.base_net_total)
+		# since tax already considers discount amount
+		invoice_value_details.invoice_discount_amt = 0
+
+	invoice_value_details.round_off = invoice.base_rounding_adjustment
+	invoice_value_details.base_grand_total = abs(invoice.base_rounded_total) or abs(invoice.base_grand_total)
+	invoice_value_details.grand_total = abs(invoice.rounded_total) or abs(invoice.grand_total)
+
 	invoice_value_details = update_invoice_taxes(invoice, invoice_value_details)
-	
+
 	return invoice_value_details
 
 def update_invoice_taxes(invoice, invoice_value_details):
@@ -226,16 +249,15 @@
 	for t in invoice.taxes:
 		if t.account_head in gst_accounts_list:
 			if t.account_head in gst_accounts.cess_account:
+				# using after discount amt since item also uses after discount amt for cess calc
 				invoice_value_details.total_cess_amt += abs(t.base_tax_amount_after_discount_amount)
-			elif t.account_head in gst_accounts.igst_account:
-				invoice_value_details.total_igst_amt += abs(t.base_tax_amount_after_discount_amount)
-			elif t.account_head in gst_accounts.sgst_account:
-				invoice_value_details.total_sgst_amt += abs(t.base_tax_amount_after_discount_amount)
-			elif t.account_head in gst_accounts.cgst_account:
-				invoice_value_details.total_cgst_amt += abs(t.base_tax_amount_after_discount_amount)
+
+			for tax_type in ['igst', 'cgst', 'sgst']:
+				if t.account_head in gst_accounts[f'{tax_type}_account']:
+					invoice_value_details[f'total_{tax_type}_amt'] += abs(t.base_tax_amount_after_discount_amount)
 		else:
 			invoice_value_details.total_other_charges += abs(t.base_tax_amount_after_discount_amount)
-	
+
 	return invoice_value_details
 
 def get_payment_details(invoice):
@@ -273,7 +295,25 @@
 		vehicle_type=vehicle_type[invoice.gst_vehicle_type]
 	))
 
+def validate_mandatory_fields(invoice):
+	if not invoice.company_address:
+		frappe.throw(_('Company Address is mandatory to fetch company GSTIN details.'), title=_('Missing Fields'))
+	if not invoice.customer_address:
+		frappe.throw(_('Customer Address is mandatory to fetch customer GSTIN details.'), title=_('Missing Fields'))
+	if not frappe.db.get_value('Address', invoice.company_address, 'gstin'):
+		frappe.throw(
+			_('GSTIN is mandatory to fetch company GSTIN details. Please enter GSTIN in selected company address.'),
+			title=_('Missing Fields')
+		)
+	if invoice.gst_category != 'Overseas' and not frappe.db.get_value('Address', invoice.customer_address, 'gstin'):
+		frappe.throw(
+			_('GSTIN is mandatory to fetch customer GSTIN details. Please enter GSTIN in selected customer address.'),
+			title=_('Missing Fields')
+		)
+
 def make_einvoice(invoice):
+	validate_mandatory_fields(invoice)
+
 	schema = read_json('einv_template')
 
 	transaction_details = get_transaction_details(invoice)
@@ -289,20 +329,23 @@
 		place_of_supply = get_place_of_supply(invoice, invoice.doctype) or invoice.billing_address_gstin
 		place_of_supply = place_of_supply[:2]
 		buyer_details.update(dict(place_of_supply=place_of_supply))
-	
+
 	shipping_details = payment_details = prev_doc_details = eway_bill_details = frappe._dict({})
 	if invoice.shipping_address_name and invoice.customer_address != invoice.shipping_address_name:
-		shipping_details = get_party_details(invoice.shipping_address_name)
-	
+		if invoice.gst_category == 'Overseas':
+			shipping_details = get_overseas_address_details(invoice.shipping_address_name)
+		else:
+			shipping_details = get_party_details(invoice.shipping_address_name)
+
 	if invoice.is_pos and invoice.base_paid_amount:
 		payment_details = get_payment_details(invoice)
-	
+
 	if invoice.is_return and invoice.return_against:
 		prev_doc_details = get_return_doc_reference(invoice)
-	
+
 	if invoice.transporter:
 		eway_bill_details = get_eway_bill_details(invoice)
-	
+
 	# not yet implemented
 	dispatch_details = period_details = export_details = frappe._dict({})
 
@@ -314,7 +357,7 @@
 		export_details=export_details, eway_bill_details=eway_bill_details
 	)
 	einvoice = json.loads(einvoice)
-	
+
 	validations = json.loads(read_json('einv_validation'))
 	errors = validate_einvoice(validations, einvoice)
 	if errors:
@@ -352,13 +395,14 @@
 					# remove empty dicts
 					einvoice.pop(fieldname, None)
 			continue
-		
+
 		# convert to int or str
 		if value_type == 'string':
 			einvoice[fieldname] = str(value)
 		elif value_type == 'number':
 			is_integer = '.' not in str(field_validation.get('maximum'))
-			einvoice[fieldname] = flt(value, 2) if not is_integer else cint(value)
+			precision = 3 if '.999' in str(field_validation.get('maximum')) else 2
+			einvoice[fieldname] = flt(value, precision) if not is_integer else cint(value)
 			value = einvoice[fieldname]
 
 		max_length = field_validation.get('maxLength')
@@ -375,7 +419,7 @@
 			errors.append(_('{} {} should be between {} and {}').format(label, value, minimum, maximum))
 		if pattern_str and not pattern.match(value):
 			errors.append(field_validation.get('validationMsg'))
-	
+
 	return errors
 
 class RequestFailed(Exception): pass
@@ -383,38 +427,44 @@
 class GSPConnector():
 	def __init__(self, doctype=None, docname=None):
 		self.e_invoice_settings = frappe.get_cached_doc('E Invoice Settings')
+		sandbox_mode = self.e_invoice_settings.sandbox_mode
+
 		self.invoice = frappe.get_cached_doc(doctype, docname) if doctype and docname else None
 		self.credentials = self.get_credentials()
 
-		self.base_url = 'https://gsp.adaequare.com/'
-		self.authenticate_url = self.base_url + 'gsp/authenticate?grant_type=token'
-		self.gstin_details_url = self.base_url + 'test/enriched/ei/api/master/gstin'
-		self.generate_irn_url = self.base_url + 'test/enriched/ei/api/invoice'
-		self.irn_details_url = self.base_url + 'test/enriched/ei/api/invoice/irn'
-		self.cancel_irn_url = self.base_url + 'test/enriched/ei/api/invoice/cancel'
-		self.cancel_ewaybill_url = self.base_url + '/test/enriched/ei/api/ewayapi'
-		self.generate_ewaybill_url = self.base_url + 'test/enriched/ei/api/ewaybill'
-	
+		# authenticate url is same for sandbox & live
+		self.authenticate_url = 'https://gsp.adaequare.com/gsp/authenticate?grant_type=token'
+		self.base_url = 'https://gsp.adaequare.com' if not sandbox_mode else 'https://gsp.adaequare.com/test'
+
+		self.cancel_irn_url = self.base_url + '/enriched/ei/api/invoice/cancel'
+		self.irn_details_url = self.base_url + '/enriched/ei/api/invoice/irn'
+		self.generate_irn_url = self.base_url + '/enriched/ei/api/invoice'
+		self.gstin_details_url = self.base_url + '/enriched/ei/api/master/gstin'
+		self.cancel_ewaybill_url = self.base_url + '/enriched/ewb/ewayapi?action=CANEWB'
+		self.generate_ewaybill_url = self.base_url + '/enriched/ei/api/ewaybill'
+
 	def get_credentials(self):
 		if self.invoice:
 			gstin = self.get_seller_gstin()
+			if not self.e_invoice_settings.enable:
+				frappe.throw(_("E-Invoicing is disabled. Please enable it from {} to generate e-invoices.").format(get_link_to_form("E Invoice Settings", "E Invoice Settings")))
 			credentials = next(d for d in self.e_invoice_settings.credentials if d.gstin == gstin)
 		else:
 			credentials = self.e_invoice_settings.credentials[0] if self.e_invoice_settings.credentials else None
 		return credentials
-	
+
 	def get_seller_gstin(self):
 		gstin = self.invoice.company_gstin or frappe.db.get_value('Address', self.invoice.company_address, 'gstin')
 		if not gstin:
 			frappe.throw(_('Cannot retrieve Company GSTIN. Please select company address with valid GSTIN.'))
 		return gstin
-	
+
 	def get_auth_token(self):
 		if time_diff_in_seconds(self.e_invoice_settings.token_expiry, now_datetime()) < 150.0:
 			self.fetch_auth_token()
-		
+
 		return self.e_invoice_settings.auth_token
-	
+
 	def make_request(self, request_type, url, headers=None, data=None):
 		if request_type == 'post':
 			res = make_post_request(url, headers=headers, data=data)
@@ -423,7 +473,7 @@
 
 		self.log_request(url, headers, data, res)
 		return res
-	
+
 	def log_request(self, url, headers, data, res):
 		headers.update({ 'password': self.credentials.password })
 		request_log = frappe.get_doc({
@@ -435,7 +485,7 @@
 			"data": json.dumps(data, indent=4) if isinstance(data, dict) else data,
 			"response": json.dumps(res, indent=4) if res else None
 		})
-		request_log.insert(ignore_permissions=True)
+		request_log.save(ignore_permissions=True)
 		frappe.db.commit()
 
 	def fetch_auth_token(self):
@@ -448,12 +498,13 @@
 			res = self.make_request('post', self.authenticate_url, headers)
 			self.e_invoice_settings.auth_token = "{} {}".format(res.get('token_type'), res.get('access_token'))
 			self.e_invoice_settings.token_expiry = add_to_date(None, seconds=res.get('expires_in'))
-			self.e_invoice_settings.save()
+			self.e_invoice_settings.save(ignore_permissions=True)
+			self.e_invoice_settings.reload()
 
 		except Exception:
 			self.log_error(res)
 			self.raise_error(True)
-	
+
 	def get_headers(self):
 		return {
 			'content-type': 'application/json',
@@ -475,14 +526,13 @@
 			else:
 				self.log_error(res)
 				raise RequestFailed
-		
+
 		except RequestFailed:
 			self.raise_error()
 
 		except Exception:
 			self.log_error()
 			self.raise_error(True)
-	
 	@staticmethod
 	def get_gstin_details(gstin):
 		'''fetch and cache GSTIN details'''
@@ -520,7 +570,7 @@
 
 			else:
 				raise RequestFailed
-		
+
 		except RequestFailed:
 			errors = self.sanitize_error_message(res.get('message'))
 			self.raise_error(errors=errors)
@@ -528,7 +578,7 @@
 		except Exception:
 			self.log_error(data)
 			self.raise_error(True)
-	
+
 	def get_irn_details(self, irn):
 		headers = self.get_headers()
 
@@ -539,7 +589,7 @@
 				return res.get('result')
 			else:
 				raise RequestFailed
-		
+
 		except RequestFailed:
 			errors = self.sanitize_error_message(res.get('message'))
 			self.raise_error(errors=errors)
@@ -547,7 +597,7 @@
 		except Exception:
 			self.log_error()
 			self.raise_error(True)
-	
+
 	def cancel_irn(self, irn, reason, remark):
 		headers = self.get_headers()
 		data = json.dumps({
@@ -569,7 +619,7 @@
 
 			else:
 				raise RequestFailed
-		
+
 		except RequestFailed:
 			errors = self.sanitize_error_message(res.get('message'))
 			self.raise_error(errors=errors)
@@ -577,7 +627,6 @@
 		except Exception:
 			self.log_error(data)
 			self.raise_error(True)
-	
 	def generate_eway_bill(self, **kwargs):
 		args = frappe._dict(kwargs)
 
@@ -618,7 +667,7 @@
 		except Exception:
 			self.log_error(data)
 			self.raise_error(True)
-	
+
 	def cancel_eway_bill(self, eway_bill, reason, remark):
 		headers = self.get_headers()
 		data = json.dumps({
@@ -626,7 +675,8 @@
 			'cancelRsnCode': reason,
 			'cancelRmrk': remark
 		}, indent=4)
-
+		headers["username"] = headers["user_name"]
+		del headers["user_name"]
 		try:
 			res = self.make_request('post', self.cancel_ewaybill_url, headers, data)
 			if res.get('success'):
@@ -649,7 +699,7 @@
 		except Exception:
 			self.log_error(data)
 			self.raise_error(True)
-	
+
 	def sanitize_error_message(self, message):
 		'''
 			On validation errors, response message looks something like this:
@@ -688,7 +738,7 @@
 			"Exception:", err_tb
 		])
 		frappe.log_error(title=_('E Invoice Request Failed'), message=message)
-	
+
 	def raise_error(self, raise_exception=False, errors=[]):
 		title = _('E Invoice Request Failed')
 		if errors:
@@ -701,7 +751,7 @@
 				raise_exception=raise_exception,
 				indicator='red'
 			)
-	
+
 	def set_einvoice_data(self, res):
 		enc_signed_invoice = res.get('SignedInvoice')
 		dec_signed_invoice = jwt.decode(enc_signed_invoice, verify=False)['data']
@@ -719,28 +769,27 @@
 			'label': _('IRN Generated')
 		}
 		self.update_invoice()
-	
 	def attach_qrcode_image(self):
 		qrcode = self.invoice.signed_qr_code
 		doctype = self.invoice.doctype
 		docname = self.invoice.name
+		filename = 'QRCode_{}.png'.format(docname).replace(os.path.sep, "__")
 
-		_file = frappe.new_doc('File')
-		_file.update({
-			'file_name': f'QRCode_{docname}.png',
-			'attached_to_doctype': doctype,
-			'attached_to_name': docname,
-			'content': 'qrcode',
-			'is_private': 1
-		})
-		_file.insert()
-		frappe.db.commit()
+		qr_image = io.BytesIO()
 		url = qrcreate(qrcode, error='L')
-		abs_file_path = os.path.abspath(_file.get_full_path())
-		url.png(abs_file_path, scale=2, quiet_zone=1)
-
+		url.png(qr_image, scale=2, quiet_zone=1)
+		_file = frappe.get_doc({
+			"doctype": "File",
+			"file_name": filename,
+			"attached_to_doctype": doctype,
+			"attached_to_name": docname,
+			"attached_to_field": "qrcode_image",
+			"is_private": 1,
+			"content": qr_image.getvalue()})
+		_file.save()
+		frappe.db.commit()
 		self.invoice.qrcode_image = _file.file_url
-	
+
 	def update_invoice(self):
 		self.invoice.flags.ignore_validate_update_after_submit = True
 		self.invoice.flags.ignore_validate = True
@@ -769,4 +818,4 @@
 @frappe.whitelist()
 def cancel_eway_bill(doctype, docname, eway_bill, reason, remark):
 	gsp_connector = GSPConnector(doctype, docname)
-	gsp_connector.cancel_eway_bill(eway_bill, reason, remark)
\ No newline at end of file
+	gsp_connector.cancel_eway_bill(eway_bill, reason, remark)
diff --git a/erpnext/regional/india/gst_state_code_data.json b/erpnext/regional/india/gst_state_code_data.json
index ff88e0f..8481c27 100644
--- a/erpnext/regional/india/gst_state_code_data.json
+++ b/erpnext/regional/india/gst_state_code_data.json
@@ -168,5 +168,10 @@
   "state_number": "37",
   "state_code": "AD",
   "state_name": "Andhra Pradesh (New)"
+ },
+ {
+  "state_number": "38",
+  "state_code": "LA",
+  "state_name": "Ladakh"
  }
 ]
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index 5321a9a..40247f7 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -7,7 +7,7 @@
 from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 from frappe.permissions import add_permission, update_permission_property
 from erpnext.regional.india import states
-from erpnext.accounts.utils import get_fiscal_year
+from erpnext.accounts.utils import get_fiscal_year, FiscalYearError
 from frappe.utils import today
 
 def setup(company=None, patch=True):
@@ -398,9 +398,9 @@
 	si_einvoice_fields = [
 		dict(fieldname='irn', label='IRN', fieldtype='Data', read_only=1, insert_after='customer', no_copy=1, print_hide=1,
 			depends_on='eval:in_list(["Registered Regular", "SEZ", "Overseas", "Deemed Export"], doc.gst_category) && doc.irn_cancelled === 0'),
-		
+
 		dict(fieldname='ack_no', label='Ack. No.', fieldtype='Data', read_only=1, hidden=1, insert_after='irn', no_copy=1, print_hide=1),
-		
+
 		dict(fieldname='ack_date', label='Ack. Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_no', no_copy=1, print_hide=1),
 
 		dict(fieldname='irn_cancelled', label='IRN Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
@@ -498,6 +498,14 @@
 				fieldtype='Link', options='Salary Component', insert_after='basic_component'),
 			dict(fieldname='arrear_component', label='Arrear Component',
 				fieldtype='Link', options='Salary Component', insert_after='hra_component'),
+			dict(fieldname='non_profit_section', label='Non Profit Settings',
+				fieldtype='Section Break', insert_after='asset_received_but_not_billed', collapsible=1),
+			dict(fieldname='company_80g_number', label='80G Number',
+				fieldtype='Data', insert_after='non_profit_section'),
+			dict(fieldname='with_effect_from', label='80G With Effect From',
+				fieldtype='Date', insert_after='company_80g_number'),
+			dict(fieldname='pan_details', label='PAN Number',
+				fieldtype='Data', insert_after='with_effect_from')
 		],
 		'Employee Tax Exemption Declaration':[
 			dict(fieldname='hra_section', label='HRA Exemption',
@@ -580,7 +588,15 @@
 				'options': '\nWith Payment of Tax\nWithout Payment of Tax'
 			}
 		],
-		"Member": [
+		'Member': [
+			{
+				'fieldname': 'pan_number',
+				'label': 'PAN Details',
+				'fieldtype': 'Data',
+				'insert_after': 'email_id'
+			}
+		],
+		'Donor': [
 			{
 				'fieldname': 'pan_number',
 				'label': 'PAN Details',
@@ -629,13 +645,18 @@
 
 def set_tax_withholding_category(company):
 	accounts = []
+	fiscal_year = None
 	abbr = frappe.get_value("Company", company, "abbr")
 	tds_account = frappe.get_value("Account", 'TDS Payable - {0}'.format(abbr), 'name')
 
 	if company and tds_account:
 		accounts = [dict(company=company, account=tds_account)]
 
-	fiscal_year = get_fiscal_year(today(), company=company)[0]
+	try:
+		fiscal_year = get_fiscal_year(today(), verbose=0, company=company)[0]
+	except FiscalYearError:
+		pass
+
 	docs = get_tds_details(accounts, fiscal_year)
 
 	for d in docs:
@@ -650,11 +671,14 @@
 			if accounts:
 				doc.append("accounts", accounts[0])
 
-			# if fiscal year don't match with any of the already entered data, append rate row
-			fy_exist = [k for k in doc.get('rates') if k.get('fiscal_year')==fiscal_year]
-			if not fy_exist:
-				doc.append("rates", d.get('rates')[0])
+			if fiscal_year:
+				# if fiscal year don't match with any of the already entered data, append rate row
+				fy_exist = [k for k in doc.get('rates') if k.get('fiscal_year')==fiscal_year]
+				if not fy_exist:
+					doc.append("rates", d.get('rates')[0])
 
+			doc.flags.ignore_permissions = True
+			doc.flags.ignore_mandatory = True
 			doc.save()
 
 def set_tds_account(docs, company):
diff --git a/erpnext/regional/india/taxes.js b/erpnext/regional/india/taxes.js
index 87baece..f09d3d0 100644
--- a/erpnext/regional/india/taxes.js
+++ b/erpnext/regional/india/taxes.js
@@ -40,14 +40,12 @@
 				callback: function(r) {
 					if(r.message) {
 						frm.set_value('taxes_and_charges', r.message.taxes_and_charges);
+						frm.set_value('taxes', r.message.taxes);
 						frm.set_value('place_of_supply', r.message.place_of_supply);
-					} else if (frm.doc.is_internal_supplier || frm.doc.is_internal_customer) {
-						frm.set_value('taxes_and_charges', '');
-						frm.set_value('taxes', []);
 					}
 				}
 			});
 		}
 	});
-};
+}
 
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 7f00d1e..3757f63 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -48,6 +48,9 @@
 		validate_gstin_check_digit(doc.gstin)
 		set_gst_state_and_state_number(doc)
 
+		if not doc.gst_state:
+			frappe.throw(_("Please Enter GST state"))
+
 		if doc.gst_state_number != doc.gstin[:2]:
 			frappe.throw(_("Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.")
 				.format(doc.gst_state_number))
@@ -168,7 +171,7 @@
 
 	if is_internal_transfer(party_details, doctype):
 		party_details.taxes_and_charges = ''
-		party_details.taxes = ''
+		party_details.taxes = []
 		return party_details
 
 	if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"):
@@ -690,25 +693,12 @@
 	if country != 'India':
 		return
 
-	if not doc.total_taxes_and_charges:
+	gst_tax, base_gst_tax = get_gst_tax_amount(doc)
+
+	if not base_gst_tax:
 		return
 
 	if doc.reverse_charge == 'Y':
-		gst_accounts = get_gst_accounts(doc.company)
-		gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \
-			+ gst_accounts.get('igst_account')
-
-		base_gst_tax = 0
-		gst_tax = 0
-
-		for tax in doc.get('taxes'):
-			if tax.category not in ("Total", "Valuation and Total"):
-				continue
-
-			if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in gst_account_list:
-				base_gst_tax += tax.base_tax_amount_after_discount_amount
-				gst_tax += tax.tax_amount_after_discount_amount
-
 		doc.taxes_and_charges_added -= gst_tax
 		doc.total_taxes_and_charges -= gst_tax
 		doc.base_taxes_and_charges_added -= base_gst_tax
@@ -742,7 +732,9 @@
 	if country != 'India':
 		return gl_entries
 
-	if not doc.total_taxes_and_charges:
+	gst_tax, base_gst_tax = get_gst_tax_amount(doc)
+
+	if not base_gst_tax:
 		return gl_entries
 
 	if doc.reverse_charge == 'Y':
diff --git a/erpnext/regional/print_format/80g_certificate_for_donation/80g_certificate_for_donation.json b/erpnext/regional/print_format/80g_certificate_for_donation/80g_certificate_for_donation.json
new file mode 100644
index 0000000..a8da0bd
--- /dev/null
+++ b/erpnext/regional/print_format/80g_certificate_for_donation/80g_certificate_for_donation.json
@@ -0,0 +1,26 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2021-02-22 00:17:33.878581",
+ "css": ".details {\n    font-size: 15px;\n    font-family: Tahoma, sans-serif;;\n    line-height: 150%;\n}\n\n.certificate-footer {\n    font-size: 15px;\n    font-family: Tahoma, sans-serif;\n    line-height: 140%;\n    margin-top: 120px;\n}\n\n.company-address {\n    color: #666666;\n    font-size: 15px;\n    font-family: Tahoma, sans-serif;;\n}",
+ "custom_format": 1,
+ "default_print_language": "en",
+ "disabled": 0,
+ "doc_type": "Tax Exemption 80G Certificate",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Default",
+ "html": "{% if letter_head and not no_letterhead -%}\n    <div class=\"letter-head\">{{ letter_head }}</div>\n{%- endif %}\n\n<div>\n    <h3 class=\"text-center\">{{ doc.company }} 80G Donor Certificate</h3>\n</div>\n<br><br>\n\n<div class=\"details\">\n    <p> <b>{{ _(\"Certificate No. : \") }}</b> {{ doc.name }} </p>\n    <p>\n    \t<b>{{ _(\"Date\") }} :</b> {{ doc.get_formatted(\"date\") }}<br>\n    </p>\n    <br><br>\n    \n    <div>\n\n        This is to confirm that the {{ doc.company }} received an amount of <b>{{doc.get_formatted(\"amount\")}}</b>\n        from <b>{{ doc.donor_name }}</b>\n        {% if doc.pan_number -%}\n            bearing PAN Number {{ doc.member_pan_number }}\n        {%- endif %}\n\n        via the Mode of Payment {{doc.mode_of_payment}}\n\n        {% if doc.razorpay_payment_id -%}\n            bearing RazorPay Payment ID {{ doc.razorpay_payment_id }}\n        {%- endif %}\n\n        on {{ doc.get_formatted(\"date_of_donation\") }}\n        <br><br>\n        \n        <p>\n            We thank you for your contribution towards the corpus of the {{ doc.company }} and helping support our work.\n        </p>\n\n    </div>\n</div>\n\n<br><br>\n<p class=\"company-address text-left\"> {{doc.company_address_display }}</p>\n\n<div class=\"certificate-footer text-center\">\n    <p><i>Computer generated receipt - Does not require signature</i></p><br>\n    \n    {% if doc.company_pan_number %}\n    <p>\n        <b>{{ doc.company }}'s PAN Account No :</b> {{ doc.company_pan_number }}\n    <p><br>\n    {% endif %}\n    \n    <p>\n        <b>80G Number : </b> {{ doc.company_80g_number }}\n        {% if doc.company_80g_wef %}\n            ( w.e.f. {{ doc.get_formatted('company_80g_wef') }} )\n        {% endif %}\n    </p><br>\n</div>",
+ "idx": 0,
+ "line_breaks": 0,
+ "modified": "2021-02-22 00:20:08.516600",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "80G Certificate for Donation",
+ "owner": "Administrator",
+ "print_format_builder": 0,
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_section_headings": 0,
+ "standard": "Yes"
+}
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/regional/print_format/80g_certificate_for_donation/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/regional/print_format/80g_certificate_for_donation/__init__.py
diff --git a/erpnext/regional/print_format/80g_certificate_for_membership/80g_certificate_for_membership.json b/erpnext/regional/print_format/80g_certificate_for_membership/80g_certificate_for_membership.json
new file mode 100644
index 0000000..f1b15aa
--- /dev/null
+++ b/erpnext/regional/print_format/80g_certificate_for_membership/80g_certificate_for_membership.json
@@ -0,0 +1,26 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2021-02-15 16:53:55.026611",
+ "css": ".details {\n    font-size: 15px;\n    font-family: Tahoma, sans-serif;;\n    line-height: 150%;\n}\n\n.certificate-footer {\n    font-size: 15px;\n    font-family: Tahoma, sans-serif;\n    line-height: 140%;\n    margin-top: 120px;\n}\n\n.company-address {\n    color: #666666;\n    font-size: 15px;\n    font-family: Tahoma, sans-serif;;\n}",
+ "custom_format": 1,
+ "default_print_language": "en",
+ "disabled": 0,
+ "doc_type": "Tax Exemption 80G Certificate",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Default",
+ "html": "{% if letter_head and not no_letterhead -%}\n    <div class=\"letter-head\">{{ letter_head }}</div>\n{%- endif %}\n\n<div>\n    <h3 class=\"text-center\">{{ doc.company }} Members 80G Donor Certificate</h3>\n    <h3 class=\"text-center\">Financial Cycle {{ doc.fiscal_year }}</h3>\n</div>\n<br><br>\n\n<div class=\"details\">\n    <p> <b>{{ _(\"Certificate No. : \") }}</b> {{ doc.name }} </p>\n    <p>\n    \t<b>{{ _(\"Date\") }} :</b> {{ doc.get_formatted(\"date\") }}<br>\n    </p>\n    <br><br>\n    \n    <div>\n        This is to confirm that the {{ doc.company }} received a total amount of <b>{{doc.get_formatted(\"total\")}}</b>\n        from <b>{{ doc.member_name }}</b>\n        {% if doc.pan_number -%}\n            bearing PAN Number {{ doc.member_pan_number }}\n        {%- endif %}\n        as per the payment details given below:\n        \n        <br><br>\n        <table class=\"table table-bordered table-condensed\">\n        \t<thead>\n        \t\t<tr>\n        \t\t\t<th >{{ _(\"Date\") }}</th>\n        \t\t\t<th class=\"text-right\">{{ _(\"Amount\") }}</th>\n        \t\t\t<th class=\"text-right\">{{ _(\"Invoice ID\") }}</th>\n        \t\t</tr>\n        \t</thead>\n        \t<tbody>\n        \t\t{%- for payment in doc.payments -%}\n        \t\t<tr>\n        \t\t\t<td> {{ payment.date }} </td>\n        \t\t\t<td class=\"text-right\">{{ payment.get_formatted(\"amount\") }}</td>\n        \t\t\t<td class=\"text-right\">{{ payment.invoice_id }}</td>\n        \t\t</tr>\n        \t\t{%- endfor -%}\n        \t</tbody>\n        </table>\n        \n        <br>\n        \n        <p>\n            We thank you for your contribution towards the corpus of the {{ doc.company }} and helping support our work.\n        </p>\n\n    </div>\n</div>\n\n<br><br>\n<p class=\"company-address text-left\"> {{doc.company_address_display }}</p>\n\n<div class=\"certificate-footer text-center\">\n    <p><i>Computer generated receipt - Does not require signature</i></p><br>\n    \n    {% if doc.company_pan_number %}\n    <p>\n        <b>{{ doc.company }}'s PAN Account No :</b> {{ doc.company_pan_number }}\n    <p><br>\n    {% endif %}\n    \n    <p>\n        <b>80G Number : </b> {{ doc.company_80g_number }}\n        {% if doc.company_80g_wef %}\n            ( w.e.f. {{ doc.get_formatted('company_80g_wef') }} )\n        {% endif %}\n    </p><br>\n</div>",
+ "idx": 0,
+ "line_breaks": 0,
+ "modified": "2021-02-21 23:29:00.778973",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "80G Certificate for Membership",
+ "owner": "Administrator",
+ "print_format_builder": 0,
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_section_headings": 0,
+ "standard": "Yes"
+}
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/regional/print_format/80g_certificate_for_membership/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/regional/print_format/80g_certificate_for_membership/__init__.py
diff --git a/erpnext/regional/print_format/irs_1099_form/irs_1099_form.json b/erpnext/regional/print_format/irs_1099_form/irs_1099_form.json
index ce8c44a..e59700f 100644
--- a/erpnext/regional/print_format/irs_1099_form/irs_1099_form.json
+++ b/erpnext/regional/print_format/irs_1099_form/irs_1099_form.json
@@ -1,23 +1,26 @@
-[
- {
-	"align_labels_right": 0,
-	"css": "",
-	"custom_format": 1,
-	"default_print_language": "en",
-	"disabled": 0,
-	"doc_type": "Supplier",
-	"docstatus": 0,
-	"doctype": "Print Format",
-	"font": "Default",
-	"format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"<div class=\\\"print-heading\\\">\\t\\t\\t\\t<h2>TAX Invoice<br><small>{{ doc.name }}</small>\\t\\t\\t\\t</h2></div>\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"customer_name\", \"label\": \"Customer Name\"}, {\"print_hide\": 0, \"fieldname\": \"customer_name_in_arabic\", \"label\": \"Customer Name in Arabic\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"posting_date\", \"label\": \"Date\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Address\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"company\", \"label\": \"Company\"}, {\"print_hide\": 0, \"fieldname\": \"company_trn\", \"label\": \"Company TRN\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"company_address_display\", \"label\": \"Company Address\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"item_code\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"200px\"}, {\"print_hide\": 0, \"fieldname\": \"uom\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"tax_code\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"items\", \"label\": \"Items\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"total\", \"label\": \"Total\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"charge_type\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"row_id\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"account_head\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"cost_center\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"300px\"}, {\"print_hide\": 0, \"fieldname\": \"rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"tax_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"total\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"tax_amount_after_discount_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"base_tax_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"base_total\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"base_tax_amount_after_discount_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"item_wise_tax_detail\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"taxes\", \"label\": \"Sales Taxes and Charges\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"grand_total\", \"label\": \"Grand Total\"}, {\"print_hide\": 0, \"fieldname\": \"rounded_total\", \"label\": \"Rounded Total\"}, {\"print_hide\": 0, \"fieldname\": \"in_words\", \"align\": \"left\", \"label\": \"In Words\"}]",
-	"html": "<div id=\"copy_a\" style=\"position: relative; top:0cm; width:17cm;height:28.0cm;\">\n  <table>\n  <tbody>\n    <tr style=\"height:12mm\">\n      <td class=\"tbs rbs lbs bbs\" style=\"width:86mm\" colspan=\"4\"; rowspan=\"3\">PAYER'S name, street address, city or town, state or province, country, ZIP<br>or foreign postal code, and telephone no.<br>\n\t{{company if company else \"\"}}<br>\n\t{{payer_street_address if payer_street_address else \"\"}}\n</td>\n      <td class=\"tbs rbs lbs bbs\" style=\"width:35mm\">1 Rents</td>\n      <td class=\"tbs rbs lbs bbs\" style=\"width:25mm\" rowspan=\"2\">OMB No. 1545-0115<br><yone>20</yone><ytwo>18</ytwo><br>Form 1099-MISC</td>\n      <td class=\"lbs bbs\" style=\"width:38mm\" colspan=\"2\" rowspan=\"2\">Miscellaneous Income</td>\n    </tr>\n    <tr style=\"height:12mm\">\n      <td class=\"tbs rbs lbs bbs\" style=\"width:35mm\">2 Royalties</td>\n    </tr>\n    <tr style=\"height:9mm\">\n      <td class=\"tbs rbs lbs bbs\" >3 Other Income<br>\n\t{{payments if payments else \"\"}}\n\t</td>\n      <td class=\"tbs rbs lbs bbs\" colspan=\"2\">4 Federal Income tax withheld</td>\n      <td class=\"tbs lbs bbs\" style=\"width:29mm\" rowspan=\"2\">Copy A<br>For<br>Internal Revenue<br>Service Center<br><br>File with Form 1096</td>\n    </tr>\n    <tr style=\"height:16mm\">\n      <td class=\"tbs rbs lbs bbs\" style=\"width:43mm\">PAYER'S TIN<br>\n\t{{company_tin if company_tin else \"\"}}\n\t</td>\n      \n      <td class=\"tbs rbs lbs bbs\" colspan=\"3\">RECIPIENT'S TIN<br><br>\n      {{tax_id if tax_id else \"None\"}}\n</td>\n      <td class=\"tbs rbs lbs bbs\" >Fishing boat proceeds</td>\n      <td class=\"tbs rbs lbs bbs\" colspan=\"2\">6 Medical and health care payments</td>\n    </tr>\n    <tr style=\"height:12mm\">\n      <td class=\"tbs rbs lbs bbs\" colspan=\"4\">RECIPIENT'S name <br>\n         {{supplier if supplier else \"\"}}\n      </td>\n      <td class=\"tbs rbs lbs bbs\" >7 Nonemployee compensation<br>\n\t</td> \n      <td class=\"tbs rbs lbs bbs\" colspan=\"2\">Substitute payments in lieu of dividends or interest</td>\n      <td class=\"tbs lbs bbs\" rowspan=\"6\">For Privacy Act<br>and Paperwork<br>Reduction Act<br>Notice, see the<br>2018 General<br>Instructions for<br>Certain<br>Information<br>Returns.</td>\n    </tr>\n    <tr style=\"height:6mm\">\n      <td class=\"tbs rbs lbs bbs\" colspan=\"4\" rowspan=\"2\">Street address (including apt. no.)<br>\n\t{{recipient_street_address if recipient_street_address else \"\"}}\n\t</td>\n      <td class=\"tbs rbs lbs bbs\" >$___________</td>\n      <td class=\"tbs rbs lbs bbs\" colspan=\"2\">$___________</td>\n    </tr>\n    <tr style=\"height:7mm\">\n      <td class=\"tbs rbs lbs bbs\" rowspan=\"2\">9 Payer made direct sales of<br>$5,000 or more of consumer products<br>to a buyer<br>(recipient) for resale</td>\n      <td class=\"tbs rbs lbs\" colspan=\"2\">10 Crop insurance proceeds</td>\n    </tr>\n    <tr style=\"height:5mm\">\n      <td class=\"tbs rbs lbs bbs\" colspan=\"4\" rowspan=\"2\">City or town, state or province, country, and ZIP or foreign postal code<br>\n\t{{recipient_city_state if recipient_city_state else \"\"}}\n</td>\n      <td style=\"vertical-align:bottom\" class=\" rbs lbs bbs\" colspan=\"2\">$___________</td>\n    </tr>\n    <tr style=\"height:9mm\">\n      <td class=\"tbs rbs lbs bbs\" >11</td>\n      <td class=\"tbs rbs lbs bbs\" colspan=2>12</td>\n    </tr>\n    <tr style=\"height:13mm\">\n      <td class=\"tbs rbs lbs bbs\" colspan=\"2\">Account number (see instructions)</td>\n      <td class=\"tbs rbs lbs bbs\" style=\"width:16mm\">FACTA filing<br>requirement</td>\n      <td class=\"tbs rbs lbs bbs\" style=\"width:14mm\">2nd TIN not.</td>\n      <td class=\"tbs rbs lbs bbs\" >13 Excess golden parachute payments<br>$___________</td>\n      <td class=\"tbs rbs lbs bbs\" colspan=\"2\">14 Gross proceeds paid to an<br>attorney<br>$___________</td>\n    </tr>\n    <tr style=\"height:12mm\">\n      <td class=\"tbs rbs lbs \" >15a Section 409A deferrals</td>\n      <td class=\"tbs rbs lbs \" colspan=\"3\">15b Section 409 income</td>\n      <td class=\"tbs rbs lbs \" >16 State tax withheld</td>\n      <td class=\"tbs rbs lbs \" colspan=\"2\">17 State/Payer's state no.</td>\n      <td class=\"tbs lbs\" >18 State income</td>\n    </tr>\n    <tr>\n      <td class=\"lbs rbs bbs\">$</td>\n      <td class=\"lbs rbs bbs\" colspan=\"3\">$</td>\n      <td class=\"lbs rbs bbs tbd\">$</td>\n      <td class=\"lbs rbs bbs tbd\" colspan=\"2\"></td>\n      <td class=\"lbs bbs tbd\">$</td>\n    </tr>\n\n    <tr style=\"height:8mm\">\n      <td class=\"tbs\" colspan=\"8\">Form 1099-MISC Cat. No. 14425J www.irs.gov/Form1099MISC Department of the Treasury - Internal Revenue Service</td>\n    </tr>\n\n  </tbody>\n</table>\n</div>\n<div id=\"copy_1\" style=\"position: relative; top:0cm; width:17cm;height:28.0cm;\">\n  <table>\n  <tbody>\n    <tr style=\"height:12mm\">\n      <td class=\"tbs rbs lbs bbs\" style=\"width:86mm\" colspan=\"4\"; rowspan=\"3\">PAYER'S name, street address, city or town, state or province, country, ZIP<br>or foreign postal code, and telephone no.<br>\n      {{company if company else \"\"}}<br>\n    \t{{payer_street_address if payer_street_address else \"\"}}</td>\n      <td class=\"tbs rbs lbs bbs\" style=\"width:35mm\">1 Rents</td>\n      <td class=\"tbs rbs lbs bbs\" style=\"width:25mm\" rowspan=\"2\">OMB No. 1545-0115<br><yone>20</yone><ytwo>18</ytwo><br>Form 1099-MISC</td>\n      <td class=\"lbs bbs\" style=\"width:38mm\" colspan=\"2\" rowspan=\"2\">Miscellaneous Income</td>\n    </tr>\n    <tr style=\"height:12mm\">\n      <td class=\"tbs rbs lbs bbs\" style=\"width:35mm\">2 Royalties</td>\n    </tr>\n    <tr style=\"height:9mm\">\n      <td class=\"tbs rbs lbs bbs\" >3 Other Income<br>\n\t{{payments if payments else \"\"}}\n\t</td>\n      <td class=\"tbs rbs lbs bbs\" colspan=\"2\">4 Federal Income tax withheld</td>\n      <td class=\"tbs lbs bbs\" style=\"width:29mm\" rowspan=\"2\">Copy 1<br>For State Tax<br>Department</td>\n    </tr>\n    <tr style=\"height:16mm\">\n      <td class=\"tbs rbs lbs bbs\" style=\"width:43mm\">PAYER'S TIN<br>\n\t{{company_tin if company_tin else \"\"}}\n\t</td>\n      <td class=\"tbs rbs lbs bbs\" colspan=\"3\">RECIPIENT'S TIN<br>\n\t{{tax_id if tax_id else \"\"}}\n\t</td>\n      <td class=\"tbs rbs lbs bbs\" >Fishing boat proceeds</td>\n      <td class=\"tbs rbs lbs bbs\" colspan=\"2\">6 Medical and health care payments</td>\n    </tr>\n    <tr style=\"height:12mm\">\n      <td class=\"tbs rbs lbs bbs\" colspan=\"4\">RECIPIENT'S name</td>\n      {{supplier if supplier else \"\"}}\n      <td class=\"tbs rbs lbs bbs\" >7 Nonemployee compensation<br>\n\t</td>\n      <td class=\"tbs rbs lbs bbs\" colspan=\"2\">Substitute payments in lieu of dividends or interest</td>\n      <td class=\"tbs lbs bbs\" rowspan=\"6\"></td>\n    </tr>\n    <tr style=\"height:6mm\">\n      <td class=\"tbs rbs lbs bbs\" colspan=\"4\" rowspan=\"2\">Street address (including apt. no.)<br>\n\t{{recipient_street_address if recipient_street_address else \"\"}}\n\t</td>\n      <td class=\"tbs rbs lbs bbs\" >$___________</td>\n      <td class=\"tbs rbs lbs bbs\" colspan=\"2\">$___________</td>\n    </tr>\n    <tr style=\"height:7mm\">\n      <td class=\"tbs rbs lbs bbs\" rowspan=\"2\">9 Payer made direct sales of<br>$5,000 or more of consumer products<br>to a buyer<br>(recipient) for resale</td>\n      <td class=\"tbs rbs lbs\" colspan=\"2\">10 Crop insurance proceeds</td>\n    </tr>\n    <tr style=\"height:5mm\">\n      <td class=\"tbs rbs lbs bbs\" colspan=\"4\" rowspan=\"2\">City or town, state or province, country, and ZIP or foreign postal code<br>\n\t{{recipient_city_state if recipient_city_state else \"\"}}\n\t</td>\n      <td style=\"vertical-align:bottom\" class=\" rbs lbs bbs\" colspan=\"2\">$___________</td>\n    </tr>\n    <tr style=\"height:9mm\">\n      <td class=\"tbs rbs lbs bbs\" >11</td>\n      <td class=\"tbs rbs lbs bbs\" colspan=2>12</td>\n    </tr>\n    <tr style=\"height:13mm\">\n      <td class=\"tbs rbs lbs bbs\" colspan=\"2\">Account number (see instructions)</td>\n      <td class=\"tbs rbs lbs bbs\" style=\"width:16mm\">FACTA filing<br>requirement</td>\n      <td class=\"tbs rbs lbs bbs\" style=\"width:14mm\">2nd TIN not.</td>\n      <td class=\"tbs rbs lbs bbs\" >13 Excess golden parachute payments<br>$___________</td>\n      <td class=\"tbs rbs lbs bbs\" colspan=\"2\">14 Gross proceeds paid to an<br>attorney<br>$___________</td>\n    </tr>\n    <tr style=\"height:12mm\">\n      <td class=\"tbs rbs lbs \" >15a Section 409A deferrals</td>\n      <td class=\"tbs rbs lbs \" colspan=\"3\">15b Section 409 income</td>\n      <td class=\"tbs rbs lbs \" >16 State tax withheld</td>\n      <td class=\"tbs rbs lbs \" colspan=\"2\">17 State/Payer's state no.</td>\n      <td class=\"tbs lbs\" >18 State income</td>\n    </tr>\n    <tr>\n      <td class=\"lbs rbs bbs\">$</td>\n      <td class=\"lbs rbs bbs\" colspan=\"3\">$</td>\n      <td class=\"lbs rbs bbs tbd\">$</td>\n      <td class=\"lbs rbs bbs tbd\" colspan=\"2\"></td>\n      <td class=\"lbs bbs tbd\">$</td>\n    </tr>\n\n    <tr style=\"height:8mm\">\n      <td class=\"tbs\" colspan=\"8\">Form 1099-MISC Cat. No. 14425J www.irs.gov/Form1099MISC Department of the Treasury - Internal Revenue Service</td>\n    </tr>\n\n  </tbody>\n</table>\n</div>\n<style>\nbody {\n  font-family: 'Helvetica', sans-serif;\n  font-size: 5.66pt;\n}\nyone {\n  font-family: 'Helvetica', sans-serif;\n  font-size: 14pt;\n  color: black;\n  -webkit-text-fill-color: white; /* Will override color (regardless of order) */\n  -webkit-text-stroke-width: 1px;\n  -webkit-text-stroke-color: black;\n}\nytwo {\n  font-family: 'Helvetica', sans-serif;\n  font-size: 14pt;\n  color: black;\n  -webkit-text-stroke-width: 1px;\n  -webkit-text-stroke-color: black;\n}\n\ntable, th, td {\n font-family: 'Helvetica', sans-serif;\n  font-size: 5.66pt;\n    border: none;\n}\n\n.tbs {\n    border-top: 1px solid black;\n}\n\n.bbs {\n    border-bottom: 1px solid black;\n}\n.lbs {\n    border-left: 1px solid black;\n}\n.rbs {\n    border-right: 1px solid black;\n}\n.allBorder {\n    border-top: 1px solid black;\n    border-right: 1px solid black;\n    border-left: 1px solid black;\n    borter-bottom: 1px solid black;\n}\n.bottomBorderOnlyDashed {\n\tborder-bottom: 1px dashed black;\n}\n.tbd {\n\tborder-top: 1px dashed black;\n}\n.address {\n\tvertical-align: bottom;\n}\n</style>",
-	"line_breaks": 0,
-	"modified": "2018-10-08 14:56:56.912851",
-	"module": "Regional",
-	"name": "IRS 1099 Form",
-	"print_format_builder": 1,
-	"print_format_type": "Server",
-	"show_section_headings": 0,
-	"standard": "No"
- }
-]
+{
+ "align_labels_right": 0,
+ "creation": "2020-11-09 16:01:26.096002",
+ "css": "",
+ "custom_format": 1,
+ "default_print_language": "en",
+ "disabled": 0,
+ "doc_type": "Supplier",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Default",
+ "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"<div class=\\\"print-heading\\\">\\t\\t\\t\\t<h2>TAX Invoice<br><small>{{ doc.name }}</small>\\t\\t\\t\\t</h2></div>\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"customer_name\", \"label\": \"Customer Name\"}, {\"print_hide\": 0, \"fieldname\": \"customer_name_in_arabic\", \"label\": \"Customer Name in Arabic\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"posting_date\", \"label\": \"Date\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Address\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"company\", \"label\": \"Company\"}, {\"print_hide\": 0, \"fieldname\": \"company_trn\", \"label\": \"Company TRN\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"company_address_display\", \"label\": \"Company Address\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"item_code\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"200px\"}, {\"print_hide\": 0, \"fieldname\": \"uom\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"tax_code\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"items\", \"label\": \"Items\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"total\", \"label\": \"Total\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"charge_type\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"row_id\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"account_head\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"cost_center\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"300px\"}, {\"print_hide\": 0, \"fieldname\": \"rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"tax_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"total\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"tax_amount_after_discount_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"base_tax_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"base_total\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"base_tax_amount_after_discount_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"item_wise_tax_detail\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"taxes\", \"label\": \"Sales Taxes and Charges\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"grand_total\", \"label\": \"Grand Total\"}, {\"print_hide\": 0, \"fieldname\": \"rounded_total\", \"label\": \"Rounded Total\"}, {\"print_hide\": 0, \"fieldname\": \"in_words\", \"align\": \"left\", \"label\": \"In Words\"}]",
+ "html": "<div id=\"copy_a\" style=\"position: relative; top:0cm; width:17cm;height:28.0cm;\">\n  <table>\n    <tbody>\n      <tr style=\"height:12mm\">\n        <td class=\"tbs rbs lbs bbs\" style=\"width:86mm\" colspan=\"4\" ; rowspan=\"3\">PAYER'S name, street address,\n          city or town, state or province, country, ZIP<br>or foreign postal code, and telephone no.<br>\n          {{ company or \"\" }}<br>\n          {{ payer_street_address or \"\" }}\n        </td>\n        <td class=\"tbs rbs lbs bbs\" style=\"width:35mm\">1 Rents</td>\n        <td class=\"tbs rbs lbs bbs\" style=\"width:25mm\" rowspan=\"2\">OMB No. 1545-0115<br>\n          <yone>{{ fiscal_year[:2] }}</yone>\n          <ytwo>{{ fiscal_year[-2:] }}</ytwo><br>Form 1099-MISC\n        </td>\n        <td class=\"lbs bbs\" style=\"width:38mm\" colspan=\"2\" rowspan=\"2\">Miscellaneous Income</td>\n      </tr>\n      <tr style=\"height:12mm\">\n        <td class=\"tbs rbs lbs bbs\" style=\"width:35mm\">2 Royalties</td>\n      </tr>\n      <tr style=\"height:9mm\">\n        <td class=\"tbs rbs lbs bbs\">3 Other Income<br>{{ payments or \"\" }}</td>\n        <td class=\"tbs rbs lbs bbs\" colspan=\"2\">4 Federal Income tax withheld</td>\n        <td class=\"tbs lbs bbs\" style=\"width:29mm\" rowspan=\"2\">Copy A<br>For<br>Internal Revenue<br>Service\n          Center<br><br>File with Form 1096</td>\n      </tr>\n      <tr style=\"height:16mm\">\n        <td class=\"tbs rbs lbs bbs\" style=\"width:43mm\">PAYER'S TIN<br>{{ company_tin or \"\" }}</td>\n\n        <td class=\"tbs rbs lbs bbs\" colspan=\"3\">RECIPIENT'S TIN<br><br>{{ tax_id or \"None\" }}</td>\n        <td class=\"tbs rbs lbs bbs\">Fishing boat proceeds</td>\n        <td class=\"tbs rbs lbs bbs\" colspan=\"2\">6 Medical and health care payments</td>\n      </tr>\n      <tr style=\"height:12mm\">\n        <td class=\"tbs rbs lbs bbs\" colspan=\"4\">RECIPIENT'S name <br>{{ supplier or \"\" }}</td>\n        <td class=\"tbs rbs lbs bbs\">7 Nonemployee compensation<br>\n        </td>\n        <td class=\"tbs rbs lbs bbs\" colspan=\"2\">Substitute payments in lieu of dividends or interest</td>\n        <td class=\"tbs lbs bbs\" rowspan=\"6\">For Privacy Act<br>and Paperwork<br>Reduction Act<br>Notice, see\n          the<br>2018 General<br>Instructions for<br>Certain<br>Information<br>Returns.</td>\n      </tr>\n      <tr style=\"height:6mm\">\n        <td class=\"tbs rbs lbs bbs\" colspan=\"4\" rowspan=\"2\">Street address (including apt. no.)<br>\n          {{ recipient_street_address or \"\" }}\n        </td>\n        <td class=\"tbs rbs lbs bbs\">$___________</td>\n        <td class=\"tbs rbs lbs bbs\" colspan=\"2\">$___________</td>\n      </tr>\n      <tr style=\"height:7mm\">\n        <td class=\"tbs rbs lbs bbs\" rowspan=\"2\">9 Payer made direct sales of<br>$5,000 or more of consumer\n          products<br>to a buyer<br>(recipient) for resale</td>\n        <td class=\"tbs rbs lbs\" colspan=\"2\">10 Crop insurance proceeds</td>\n      </tr>\n      <tr style=\"height:5mm\">\n        <td class=\"tbs rbs lbs bbs\" colspan=\"4\" rowspan=\"2\">City or town, state or province, country, and ZIP or\n          foreign postal code<br>\n          {{ recipient_city_state or \"\" }}\n        </td>\n        <td style=\"vertical-align:bottom\" class=\" rbs lbs bbs\" colspan=\"2\">$___________</td>\n      </tr>\n      <tr style=\"height:9mm\">\n        <td class=\"tbs rbs lbs bbs\">11</td>\n        <td class=\"tbs rbs lbs bbs\" colspan=2>12</td>\n      </tr>\n      <tr style=\"height:13mm\">\n        <td class=\"tbs rbs lbs bbs\" colspan=\"2\">Account number (see instructions)</td>\n        <td class=\"tbs rbs lbs bbs\" style=\"width:16mm\">FACTA filing<br>requirement</td>\n        <td class=\"tbs rbs lbs bbs\" style=\"width:14mm\">2nd TIN not.</td>\n        <td class=\"tbs rbs lbs bbs\">13 Excess golden parachute payments<br>$___________</td>\n        <td class=\"tbs rbs lbs bbs\" colspan=\"2\">14 Gross proceeds paid to an<br>attorney<br>$___________</td>\n      </tr>\n      <tr style=\"height:12mm\">\n        <td class=\"tbs rbs lbs \">15a Section 409A deferrals</td>\n        <td class=\"tbs rbs lbs \" colspan=\"3\">15b Section 409 income</td>\n        <td class=\"tbs rbs lbs \">16 State tax withheld</td>\n        <td class=\"tbs rbs lbs \" colspan=\"2\">17 State/Payer's state no.</td>\n        <td class=\"tbs lbs\">18 State income</td>\n      </tr>\n      <tr>\n        <td class=\"lbs rbs bbs\">$</td>\n        <td class=\"lbs rbs bbs\" colspan=\"3\">$</td>\n        <td class=\"lbs rbs bbs tbd\">$</td>\n        <td class=\"lbs rbs bbs tbd\" colspan=\"2\"></td>\n        <td class=\"lbs bbs tbd\">$</td>\n      </tr>\n\n      <tr style=\"height:8mm\">\n        <td class=\"tbs\" colspan=\"8\">Form 1099-MISC Cat. No. 14425J www.irs.gov/Form1099MISC Department of the\n          Treasury - Internal Revenue Service</td>\n      </tr>\n\n    </tbody>\n  </table>\n</div>\n<div id=\"copy_1\" style=\"position: relative; top:0cm; width:17cm;height:28.0cm;\">\n  <table>\n    <tbody>\n      <tr style=\"height:12mm\">\n        <td class=\"tbs rbs lbs bbs\" style=\"width:86mm\" colspan=\"4\" ; rowspan=\"3\">PAYER'S name, street address,\n          city or town, state or province, country, ZIP<br>or foreign postal code, and telephone no.<br>\n          {{ company or \"\"}}<b r>\n          {{ payer_street_address or \"\" }}\n        </td>\n        <td class=\"tbs rbs lbs bbs\" style=\"width:35mm\">1 Rents</td>\n        <td class=\"tbs rbs lbs bbs\" style=\"width:25mm\" rowspan=\"2\">OMB No. 1545-0115<br>\n          <yone>{{ fiscal_year[:2] }}</yone>\n          <ytwo>{{ fiscal_year[-2:] }}</ytwo><br>Form 1099-MISC\n        </td>\n        <td class=\"lbs bbs\" style=\"width:38mm\" colspan=\"2\" rowspan=\"2\">Miscellaneous Income</td>\n      </tr>\n      <tr style=\"height:12mm\">\n        <td class=\"tbs rbs lbs bbs\" style=\"width:35mm\">2 Royalties</td>\n      </tr>\n      <tr style=\"height:9mm\">\n        <td class=\"tbs rbs lbs bbs\">3 Other Income<br>\n          {{ payments or \"\" }}\n        </td>\n        <td class=\"tbs rbs lbs bbs\" colspan=\"2\">4 Federal Income tax withheld</td>\n        <td class=\"tbs lbs bbs\" style=\"width:29mm\" rowspan=\"2\">Copy 1<br>For State Tax<br>Department</td>\n      </tr>\n      <tr style=\"height:16mm\">\n        <td class=\"tbs rbs lbs bbs\" style=\"width:43mm\">PAYER'S TIN<br>\n          {{ company_tin or \"\" }}\n        </td>\n        <td class=\"tbs rbs lbs bbs\" colspan=\"3\">RECIPIENT'S TIN<br>\n          {{ tax_id or \"\" }}\n        </td>\n        <td class=\"tbs rbs lbs bbs\">Fishing boat proceeds</td>\n        <td class=\"tbs rbs lbs bbs\" colspan=\"2\">6 Medical and health care payments</td>\n      </tr>\n      <tr style=\"height:12mm\">\n        <td class=\"tbs rbs lbs bbs\" colspan=\"4\">RECIPIENT'S name</td>\n        {{ supplier or \"\" }}\n        <td class=\"tbs rbs lbs bbs\">7 Nonemployee compensation<br>\n        </td>\n        <td class=\"tbs rbs lbs bbs\" colspan=\"2\">Substitute payments in lieu of dividends or interest</td>\n        <td class=\"tbs lbs bbs\" rowspan=\"6\"></td>\n      </tr>\n      <tr style=\"height:6mm\">\n        <td class=\"tbs rbs lbs bbs\" colspan=\"4\" rowspan=\"2\">Street address (including apt. no.)<br>\n          {{ recipient_street_address or \"\" }}\n        </td>\n        <td class=\"tbs rbs lbs bbs\">$___________</td>\n        <td class=\"tbs rbs lbs bbs\" colspan=\"2\">$___________</td>\n      </tr>\n      <tr style=\"height:7mm\">\n        <td class=\"tbs rbs lbs bbs\" rowspan=\"2\">9 Payer made direct sales of<br>$5,000 or more of consumer\n          products<br>to a buyer<br>(recipient) for resale</td>\n        <td class=\"tbs rbs lbs\" colspan=\"2\">10 Crop insurance proceeds</td>\n      </tr>\n      <tr style=\"height:5mm\">\n        <td class=\"tbs rbs lbs bbs\" colspan=\"4\" rowspan=\"2\">City or town, state or province, country, and ZIP or\n          foreign postal code<br>\n          {{ recipient_city_state or \"\" }}\n        </td>\n        <td style=\"vertical-align:bottom\" class=\" rbs lbs bbs\" colspan=\"2\">$___________</td>\n      </tr>\n      <tr style=\"height:9mm\">\n        <td class=\"tbs rbs lbs bbs\">11</td>\n        <td class=\"tbs rbs lbs bbs\" colspan=2>12</td>\n      </tr>\n      <tr style=\"height:13mm\">\n        <td class=\"tbs rbs lbs bbs\" colspan=\"2\">Account number (see instructions)</td>\n        <td class=\"tbs rbs lbs bbs\" style=\"width:16mm\">FACTA filing<br>requirement</td>\n        <td class=\"tbs rbs lbs bbs\" style=\"width:14mm\">2nd TIN not.</td>\n        <td class=\"tbs rbs lbs bbs\">13 Excess golden parachute payments<br>$___________</td>\n        <td class=\"tbs rbs lbs bbs\" colspan=\"2\">14 Gross proceeds paid to an<br>attorney<br>$___________</td>\n      </tr>\n      <tr style=\"height:12mm\">\n        <td class=\"tbs rbs lbs \">15a Section 409A deferrals</td>\n        <td class=\"tbs rbs lbs \" colspan=\"3\">15b Section 409 income</td>\n        <td class=\"tbs rbs lbs \">16 State tax withheld</td>\n        <td class=\"tbs rbs lbs \" colspan=\"2\">17 State/Payer's state no.</td>\n        <td class=\"tbs lbs\">18 State income</td>\n      </tr>\n      <tr>\n        <td class=\"lbs rbs bbs\">$</td>\n        <td class=\"lbs rbs bbs\" colspan=\"3\">$</td>\n        <td class=\"lbs rbs bbs tbd\">$</td>\n        <td class=\"lbs rbs bbs tbd\" colspan=\"2\"></td>\n        <td class=\"lbs bbs tbd\">$</td>\n      </tr>\n\n      <tr style=\"height:8mm\">\n        <td class=\"tbs\" colspan=\"8\">Form 1099-MISC Cat. No. 14425J www.irs.gov/Form1099MISC Department of the\n          Treasury - Internal Revenue Service</td>\n      </tr>\n\n    </tbody>\n  </table>\n</div>\n<style>\n  body {\n    font-family: 'Helvetica', sans-serif;\n    font-size: 5.66pt;\n  }\n\n  yone {\n    font-family: 'Helvetica', sans-serif;\n    font-size: 14pt;\n    color: black;\n    -webkit-text-fill-color: white;\n    /* Will override color (regardless of order) */\n    -webkit-text-stroke-width: 1px;\n    -webkit-text-stroke-color: black;\n  }\n\n  ytwo {\n    font-family: 'Helvetica', sans-serif;\n    font-size: 14pt;\n    color: black;\n    -webkit-text-stroke-width: 1px;\n    -webkit-text-stroke-color: black;\n  }\n\n  table,\n  th,\n  td {\n    font-family: 'Helvetica', sans-serif;\n    font-size: 5.66pt;\n    border: none;\n  }\n\n  .tbs {\n    border-top: 1px solid black;\n  }\n\n  .bbs {\n    border-bottom: 1px solid black;\n  }\n\n  .lbs {\n    border-left: 1px solid black;\n  }\n\n  .rbs {\n    border-right: 1px solid black;\n  }\n\n  .allBorder {\n    border-top: 1px solid black;\n    border-right: 1px solid black;\n    border-left: 1px solid black;\n    border-bottom: 1px solid black;\n  }\n\n  .bottomBorderOnlyDashed {\n    border-bottom: 1px dashed black;\n  }\n\n  .tbd {\n    border-top: 1px dashed black;\n  }\n\n  .address {\n    vertical-align: bottom;\n  }\n</style>",
+ "idx": 0,
+ "line_breaks": 0,
+ "modified": "2021-01-19 07:25:16.333666",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "IRS 1099 Form",
+ "owner": "Administrator",
+ "print_format_builder": 1,
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_section_headings": 0,
+ "standard": "No"
+}
\ No newline at end of file
diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py
index 1e39c57..cbc9478 100644
--- a/erpnext/regional/report/datev/datev.py
+++ b/erpnext/regional/report/datev/datev.py
@@ -96,6 +96,8 @@
 	"""Entry point for frappe."""
 	data = []
 	if filters and validate(filters):
+		fn = 'temporary_against_account_number'
+		filters[fn] = frappe.get_value('DATEV Settings', filters.get('company'), fn)
 		data = get_transactions(filters, as_dict=0)
 
 	return COLUMNS, data
@@ -156,11 +158,11 @@
 			case gl.debit when 0 then 'H' else 'S' end as 'Soll/Haben-Kennzeichen',
 
 			/* account number or, if empty, party account number */
-			coalesce(acc.account_number, acc_pa.account_number) as 'Konto',
+			acc.account_number as 'Konto',
 
 			/* against number or, if empty, party against number */
-			coalesce(acc_against.account_number, acc_against_pa.account_number) as 'Gegenkonto (ohne BU-Schlüssel)',
-			
+			%(temporary_against_account_number)s as 'Gegenkonto (ohne BU-Schlüssel)',
+
 			gl.posting_date as 'Belegdatum',
 			gl.voucher_no as 'Belegfeld 1',
 			LEFT(gl.remarks, 60) as 'Buchungstext',
@@ -171,27 +173,10 @@
 
 		FROM `tabGL Entry` gl
 
-			/* Statistisches Konto (Debitoren/Kreditoren) */
-			left join `tabParty Account` pa
-			on gl.against = pa.parent
-			and gl.company = pa.company
-
 			/* Kontonummer */
 			left join `tabAccount` acc 
 			on gl.account = acc.name
 
-			/* Gegenkonto-Nummer */
-			left join `tabAccount` acc_against 
-			on gl.against = acc_against.name
-
-			/* Statistische Kontonummer */
-			left join `tabAccount` acc_pa
-			on pa.account = acc_pa.name
-
-			/* Statistische Gegenkonto-Nummer */
-			left join `tabAccount` acc_against_pa 
-			on pa.account = acc_against_pa.name
-
 		WHERE gl.company = %(company)s 
 		AND DATE(gl.posting_date) >= %(from_date)s
 		AND DATE(gl.posting_date) <= %(to_date)s
@@ -347,7 +332,9 @@
 	coa = frappe.get_value('Company', company, 'chart_of_accounts')
 	filters['skr'] = '04' if 'SKR04' in coa else ('03' if 'SKR03' in coa else '')
 
-	filters['account_number_length'] = frappe.get_value('DATEV Settings', company, 'account_number_length')
+	datev_settings = frappe.get_doc('DATEV Settings', company)
+	filters['account_number_length'] = datev_settings.account_number_length
+	filters['temporary_against_account_number'] = datev_settings.temporary_against_account_number
 
 	transactions = get_transactions(filters)
 	account_names = get_account_names(filters)
diff --git a/erpnext/regional/report/datev/test_datev.py b/erpnext/regional/report/datev/test_datev.py
index 9529923..59b878e 100644
--- a/erpnext/regional/report/datev/test_datev.py
+++ b/erpnext/regional/report/datev/test_datev.py
@@ -126,7 +126,8 @@
 			"doctype": "DATEV Settings",
 			"client": company.name,
 			"client_number": "12345",
-			"consultant_number": "67890"
+			"consultant_number": "67890",
+			"temporary_against_account_number": "9999"
 		}).insert()
 
 
@@ -137,7 +138,8 @@
 		self.filters = {
 			"company": self.company.name,
 			"from_date": today(),
-			"to_date": today()
+			"to_date": today(),
+			"temporary_against_account_number": "9999"
 		}
 
 		make_datev_settings(self.company)
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index ad3de5f..09b04ff 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -236,6 +236,7 @@
 		self.cgst_sgst_invoices = []
 
 		unidentified_gst_accounts = []
+		unidentified_gst_accounts_invoice = []
 		for parent, account, item_wise_tax_detail, tax_amount in self.tax_details:
 			if account in self.gst_accounts.cess_account:
 				self.invoice_cess.setdefault(parent, tax_amount)
@@ -251,19 +252,21 @@
 						if not (cgst_or_sgst or account in self.gst_accounts.igst_account):
 							if "gst" in account.lower() and account not in unidentified_gst_accounts:
 								unidentified_gst_accounts.append(account)
+								unidentified_gst_accounts_invoice.append(parent)
 							continue
 
 						for item_code, tax_amounts in item_wise_tax_detail.items():
 							tax_rate = tax_amounts[0]
-							if cgst_or_sgst:
-								tax_rate *= 2
-								if parent not in self.cgst_sgst_invoices:
-									self.cgst_sgst_invoices.append(parent)
+							if tax_rate:
+								if cgst_or_sgst:
+									tax_rate *= 2
+									if parent not in self.cgst_sgst_invoices:
+										self.cgst_sgst_invoices.append(parent)
 
-							rate_based_dict = self.items_based_on_tax_rate\
-								.setdefault(parent, {}).setdefault(tax_rate, [])
-							if item_code not in rate_based_dict:
-								rate_based_dict.append(item_code)
+								rate_based_dict = self.items_based_on_tax_rate\
+									.setdefault(parent, {}).setdefault(tax_rate, [])
+								if item_code not in rate_based_dict:
+									rate_based_dict.append(item_code)
 					except ValueError:
 						continue
 		if unidentified_gst_accounts:
@@ -272,7 +275,7 @@
 
 		# Build itemised tax for export invoices where tax table is blank
 		for invoice, items in iteritems(self.invoice_items):
-			if invoice not in self.items_based_on_tax_rate \
+			if invoice not in self.items_based_on_tax_rate and invoice not in unidentified_gst_accounts_invoice \
 				and frappe.db.get_value(self.doctype, invoice, "export_type") == "Without Payment of Tax":
 					self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys())
 
diff --git a/erpnext/regional/report/irs_1099/irs_1099.js b/erpnext/regional/report/irs_1099/irs_1099.js
index 2d74652..070ff43 100644
--- a/erpnext/regional/report/irs_1099/irs_1099.js
+++ b/erpnext/regional/report/irs_1099/irs_1099.js
@@ -4,7 +4,7 @@
 frappe.query_reports["IRS 1099"] = {
 	"filters": [
 		{
-			"fieldname":"company",
+			"fieldname": "company",
 			"label": __("Company"),
 			"fieldtype": "Link",
 			"options": "Company",
@@ -13,7 +13,7 @@
 			"width": 80,
 		},
 		{
-			"fieldname":"fiscal_year",
+			"fieldname": "fiscal_year",
 			"label": __("Fiscal Year"),
 			"fieldtype": "Link",
 			"options": "Fiscal Year",
@@ -22,7 +22,7 @@
 			"width": 80,
 		},
 		{
-			"fieldname":"supplier_group",
+			"fieldname": "supplier_group",
 			"label": __("Supplier Group"),
 			"fieldtype": "Link",
 			"options": "Supplier Group",
@@ -32,16 +32,16 @@
 		},
 	],
 
-	onload: function(query_report) {
+	onload: function (query_report) {
 		query_report.page.add_inner_button(__("Print IRS 1099 Forms"), () => {
 			build_1099_print(query_report);
 		});
 	}
 };
 
-function build_1099_print(query_report){
+function build_1099_print(query_report) {
 	let filters = JSON.stringify(query_report.get_values());
 	let w = window.open('/api/method/erpnext.regional.report.irs_1099.irs_1099.irs_1099_print?' +
-								'&filters=' + encodeURIComponent(filters));
+		'&filters=' + encodeURIComponent(filters));
 	// w.print();
 }
diff --git a/erpnext/regional/report/irs_1099/irs_1099.py b/erpnext/regional/report/irs_1099/irs_1099.py
index d3509e5..c1c8aed 100644
--- a/erpnext/regional/report/irs_1099/irs_1099.py
+++ b/erpnext/regional/report/irs_1099/irs_1099.py
@@ -1,29 +1,34 @@
 # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
 
-from __future__ import unicode_literals
-import frappe
 import json
-from frappe import _, _dict
-from frappe.utils import nowdate
-from frappe.utils.data import fmt_money
-from erpnext.accounts.utils import get_fiscal_year
+
 from PyPDF2 import PdfFileWriter
+
+import frappe
+from erpnext.accounts.utils import get_fiscal_year
+from frappe import _
+from frappe.utils import cstr, nowdate
+from frappe.utils.data import fmt_money
+from frappe.utils.jinja import render_template
 from frappe.utils.pdf import get_pdf
 from frappe.utils.print_format import read_multi_pdf
-from frappe.utils.jinja import render_template
+
+IRS_1099_FORMS_FILE_EXTENSION = ".pdf"
 
 
 def execute(filters=None):
-	filters = filters if isinstance(filters, _dict) else _dict(filters)
-
+	filters = filters if isinstance(filters, frappe._dict) else frappe._dict(filters)
 	if not filters:
 		filters.setdefault('fiscal_year', get_fiscal_year(nowdate())[0])
 		filters.setdefault('company', frappe.db.get_default("company"))
 
-	region = frappe.db.get_value("Company", fieldname = ["country"], filters = { "name": filters.company })
+	region = frappe.db.get_value("Company",
+		filters={"name": filters.company},
+		fieldname=["country"])
+
 	if region != 'United States':
-		return [],[]
+		return [], []
 
 	data = []
 	columns = get_columns()
@@ -34,20 +39,23 @@
 			s.tax_id as "tax_id",
 			SUM(gl.debit_in_account_currency) AS "payments"
 		FROM
-			`tabGL Entry` gl INNER JOIN `tabSupplier` s
+			`tabGL Entry` gl
+				INNER JOIN `tabSupplier` s
 		WHERE
 			s.name = gl.party
-		AND	s.irs_1099 = 1
-		AND gl.fiscal_year = %(fiscal_year)s
-		AND gl.party_type = "Supplier"
-
+				AND s.irs_1099 = 1
+				AND gl.fiscal_year = %(fiscal_year)s
+				AND gl.party_type = "Supplier"
 		GROUP BY
 			gl.party
-
 		ORDER BY
-			gl.party DESC""", {"fiscal_year": filters.fiscal_year,
+			gl.party DESC
+	""", {
+		"fiscal_year": filters.fiscal_year,
 		"supplier_group": filters.supplier_group,
-		"company": filters.company}, as_dict=True)
+		"company": filters.company
+	}, as_dict=True)
+
 	return columns, data
 
 
@@ -74,7 +82,6 @@
 			"width": 120
 		},
 		{
-
 			"fieldname": "payments",
 			"label": _("Total Payments"),
 			"fieldtype": "Currency",
@@ -88,23 +95,32 @@
 	if not filters:
 		frappe._dict({
 			"company": frappe.db.get_default("Company"),
-			"fiscal_year": frappe.db.get_default("fiscal_year")})
+			"fiscal_year": frappe.db.get_default("Fiscal Year")
+		})
 	else:
 		filters = frappe._dict(json.loads(filters))
+
+	fiscal_year_doc = get_fiscal_year(fiscal_year=filters.fiscal_year, as_dict=True)
+	fiscal_year = cstr(fiscal_year_doc.year_start_date.year)
+
 	company_address = get_payer_address_html(filters.company)
 	company_tin = frappe.db.get_value("Company", filters.company, "tax_id")
+
 	columns, data = execute(filters)
 	template = frappe.get_doc("Print Format", "IRS 1099 Form").html
 	output = PdfFileWriter()
+
 	for row in data:
+		row["fiscal_year"] = fiscal_year
 		row["company"] = filters.company
 		row["company_tin"] = company_tin
 		row["payer_street_address"] = company_address
-		row["recipient_street_address"], row["recipient_city_state"] = get_street_address_html("Supplier", row.supplier)
+		row["recipient_street_address"], row["recipient_city_state"] = get_street_address_html(
+			"Supplier", row.supplier)
 		row["payments"] = fmt_money(row["payments"], precision=0, currency="USD")
-		frappe._dict(row)
 		pdf = get_pdf(render_template(template, row), output=output if output else None)
-	frappe.local.response.filename = filters.fiscal_year + " " + filters.company + " IRS 1099 Forms"
+
+	frappe.local.response.filename = f"{filters.fiscal_year} {filters.company} IRS 1099 Forms{IRS_1099_FORMS_FILE_EXTENSION}"
 	frappe.local.response.filecontent = read_multi_pdf(output)
 	frappe.local.response.type = "download"
 
@@ -120,36 +136,45 @@
 		ORDER BY
 			address_type="Postal" DESC, address_type="Billing" DESC
 		LIMIT 1
-		""", {"company": company}, as_dict=True)
+	""", {"company": company}, as_dict=True)
+
+	address_display = ""
 	if address_list:
 		company_address = address_list[0]["name"]
-		return frappe.get_doc("Address", company_address).get_display()
-	else:
-		return ""
+		address_display = frappe.get_doc("Address", company_address).get_display()
+
+	return address_display
 
 
 def get_street_address_html(party_type, party):
 	address_list = frappe.db.sql("""
 		SELECT
 			link.parent
-		FROM `tabDynamic Link` link, `tabAddress` address
-		WHERE link.parenttype = "Address"
-		AND link.link_name = %(party)s
-		ORDER BY address.address_type="Postal" DESC,
+		FROM
+			`tabDynamic Link` link,
+			`tabAddress` address
+		WHERE
+			link.parenttype = "Address"
+				AND link.link_name = %(party)s
+		ORDER BY
+			address.address_type="Postal" DESC,
 			address.address_type="Billing" DESC
 		LIMIT 1
-		""", {"party": party}, as_dict=True)
+	""", {"party": party}, as_dict=True)
+
+	street_address = city_state = ""
 	if address_list:
 		supplier_address = address_list[0]["parent"]
 		doc = frappe.get_doc("Address", supplier_address)
+
 		if doc.address_line2:
-			street = doc.address_line1 + "<br>\n" + doc.address_line2 + "<br>\n"
+			street_address = doc.address_line1 + "<br>\n" + doc.address_line2 + "<br>\n"
 		else:
-			street = doc.address_line1 + "<br>\n"
-		city = doc.city + ", " if doc.city else ""
-		city = city + doc.state + " " if doc.state else city
-		city = city + doc.pincode if doc.pincode else city
-		city += "<br>\n"
-		return street, city
-	else:
-		return "", ""
+			street_address = doc.address_line1 + "<br>\n"
+
+		city_state = doc.city + ", " if doc.city else ""
+		city_state = city_state + doc.state + " " if doc.state else city_state
+		city_state = city_state + doc.pincode if doc.pincode else city_state
+		city_state += "<br>\n"
+
+	return street_address, city_state
diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py
index 013ae5c..776a82c 100644
--- a/erpnext/regional/united_arab_emirates/setup.py
+++ b/erpnext/regional/united_arab_emirates/setup.py
@@ -110,9 +110,11 @@
 		'Purchase Order': purchase_invoice_fields + invoice_fields,
 		'Purchase Receipt': purchase_invoice_fields + invoice_fields,
 		'Sales Invoice': sales_invoice_fields + invoice_fields,
+		'POS Invoice': sales_invoice_fields + invoice_fields,
 		'Sales Order': sales_invoice_fields + invoice_fields,
 		'Delivery Note': sales_invoice_fields + invoice_fields,
 		'Sales Invoice Item': invoice_item_fields + delivery_date_field + [is_zero_rated, is_exempt],
+		'POS Invoice Item': invoice_item_fields + delivery_date_field + [is_zero_rated, is_exempt],
 		'Purchase Invoice Item': invoice_item_fields,
 		'Sales Order Item': invoice_item_fields,
 		'Delivery Note Item': invoice_item_fields,
diff --git a/erpnext/regional/united_states/test_united_states.py b/erpnext/regional/united_states/test_united_states.py
index ad95010..513570e 100644
--- a/erpnext/regional/united_states/test_united_states.py
+++ b/erpnext/regional/united_states/test_united_states.py
@@ -26,7 +26,6 @@
         make_payment_entry_to_irs_1099_supplier()
         filters = frappe._dict({"fiscal_year": "_Test Fiscal Year 2016", "company": "_Test Company 1"})
         columns, data = execute_1099_report(filters)
-        print(columns, data)
         expected_row = {'supplier': '_US 1099 Test Supplier',
                         'supplier_group': 'Services',
                         'payments': 100.0,
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 29214ee..bf8b7fc 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -84,7 +84,10 @@
 				frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account)))
 
 	def validate_internal_customer(self):
-		if self.is_internal_customer and frappe.db.get_value('Customer', {"represents_company": self.represents_company}, "name"):
+		internal_customer = frappe.db.get_value("Customer",
+			{"is_internal_customer": 1, "represents_company": self.represents_company, "name": ("!=", self.name)}, "name")
+
+		if internal_customer:
 			frappe.throw(_("Internal Customer for company {0} already exists").format(
 				frappe.bold(self.represents_company)))
 
diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js
index 661e107..5a0d9c9 100644
--- a/erpnext/selling/doctype/quotation/quotation.js
+++ b/erpnext/selling/doctype/quotation/quotation.js
@@ -7,7 +7,7 @@
 frappe.ui.form.on('Quotation', {
 	setup: function(frm) {
 		frm.custom_make_buttons = {
-			'Sales Order': 'Make Sales Order'
+			'Sales Order': 'Sales Order'
 		},
 
 		frm.set_query("quotation_to", function() {
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index 20ae19f..36b584d 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -25,7 +25,6 @@
 	def validate(self):
 		super(Quotation, self).validate()
 		self.set_status()
-		self.update_opportunity()
 		self.validate_uom_is_integer("stock_uom", "qty")
 		self.validate_valid_till()
 		self.set_customer_name()
@@ -50,21 +49,20 @@
 			lead_name, company_name = frappe.db.get_value("Lead", self.party_name, ["lead_name", "company_name"])
 			self.customer_name = company_name or lead_name
 
-	def update_opportunity(self):
+	def update_opportunity(self, status):
 		for opportunity in list(set([d.prevdoc_docname for d in self.get("items")])):
 			if opportunity:
-				self.update_opportunity_status(opportunity)
+				self.update_opportunity_status(status, opportunity)
 
 		if self.opportunity:
-			self.update_opportunity_status()
+			self.update_opportunity_status(status)
 
-	def update_opportunity_status(self, opportunity=None):
+	def update_opportunity_status(self, status, opportunity=None):
 		if not opportunity:
 			opportunity = self.opportunity
 
 		opp = frappe.get_doc("Opportunity", opportunity)
-		opp.status = None
-		opp.set_status(update=True)
+		opp.set_status(status=status, update=True)
 
 	def declare_enquiry_lost(self, lost_reasons_list, detailed_reason=None):
 		if not self.has_sales_order():
@@ -82,7 +80,7 @@
 				else:
 					frappe.throw(_("Invalid lost reason {0}, please create a new lost reason").format(frappe.bold(reason.get('lost_reason'))))
 
-			self.update_opportunity()
+			self.update_opportunity('Lost')
 			self.update_lead()
 			self.save()
 
@@ -95,7 +93,7 @@
 			self.company, self.base_grand_total, self)
 
 		#update enquiry status
-		self.update_opportunity()
+		self.update_opportunity('Quotation')
 		self.update_lead()
 
 	def on_cancel(self):
@@ -105,7 +103,7 @@
 
 		#update enquiry status
 		self.set_status(update=True)
-		self.update_opportunity()
+		self.update_opportunity('Open')
 		self.update_lead()
 
 	def print_other_charges(self,docname):
diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json
index 59ae7b2..a6785f7 100644
--- a/erpnext/selling/doctype/quotation_item/quotation_item.json
+++ b/erpnext/selling/doctype/quotation_item/quotation_item.json
@@ -47,6 +47,7 @@
   "base_amount",
   "base_net_amount",
   "pricing_rules",
+  "stock_uom_rate",
   "is_free_item",
   "section_break_43",
   "valuation_rate",
@@ -634,12 +635,20 @@
    "print_hide": 1,
    "read_only": 1,
    "report_hide": 1
+  },
+  {
+   "depends_on": "eval: doc.uom != doc.stock_uom",
+   "fieldname": "stock_uom_rate",
+   "fieldtype": "Currency",
+   "label": "Rate of Stock UOM",
+   "options": "currency",
+   "read_only": 1
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-05-19 20:48:43.222229",
+ "modified": "2021-01-30 21:39:40.174551",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Quotation Item",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index d4fb07c..78f9df9 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -171,8 +171,10 @@
 						this.frm.add_custom_button(__('Request for Raw Materials'), () => this.make_raw_material_request(), __('Create'));
 					}
 
-					// make purchase order
+					// Make Purchase Order
+					if (!this.frm.doc.is_internal_customer) {
 						this.frm.add_custom_button(__('Purchase Order'), () => this.make_purchase_order(), __('Create'));
+					}
 
 					// maintenance
 					if(flt(doc.per_delivered, 2) < 100 && (order_is_maintenance || order_is_a_custom_sale)) {
@@ -193,16 +195,15 @@
 
 					if (doc.docstatus === 1 && !doc.inter_company_order_reference) {
 						let me = this;
-						frappe.model.with_doc("Customer", me.frm.doc.customer, () => {
-							let customer = frappe.model.get_doc("Customer", me.frm.doc.customer);
-							let internal = customer.is_internal_customer;
-							let disabled = customer.disabled;
-							if (internal === 1 && disabled === 0) {
-								me.frm.add_custom_button("Inter Company Order", function() {
-									me.make_inter_company_order();
-								}, __('Create'));
-							}
-						});
+						let internal = me.frm.doc.is_internal_customer;
+						if (internal) {
+							let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Order" :
+								"Inter Company Purchase Order";
+
+							me.frm.add_custom_button(button_label, function() {
+								me.make_inter_company_order();
+							}, __('Create'));
+						}
 					}
 				}
 				// payment request
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index 3d64ac3..0a5c665 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -107,6 +107,8 @@
   "tc_name",
   "terms",
   "more_info",
+  "is_internal_customer",
+  "represents_company",
   "inter_company_order_reference",
   "project",
   "party_account_currency",
@@ -1103,7 +1105,8 @@
    "hide_days": 1,
    "hide_seconds": 1,
    "label": "Inter Company Order Reference",
-   "options": "Purchase Order"
+   "options": "Purchase Order",
+   "read_only": 1
   },
   {
    "description": "Track this Sales Order against any Project",
@@ -1455,13 +1458,29 @@
    "hide_seconds": 1,
    "label": "Skip Delivery Note",
    "print_hide": 1
+  },
+  {
+   "default": "0",
+   "fetch_from": "customer.is_internal_customer",
+   "fieldname": "is_internal_customer",
+   "fieldtype": "Check",
+   "label": "Is Internal Customer",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "customer.represents_company",
+   "fieldname": "represents_company",
+   "fieldtype": "Link",
+   "label": "Represents Company",
+   "options": "Company",
+   "read_only": 1
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 105,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-10-30 13:59:18.628077",
+ "modified": "2021-01-20 23:40:39.929296",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 04d85e5..e561291 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -14,7 +14,6 @@
 from frappe.desk.notifications import clear_doctype_notifications
 from frappe.contacts.doctype.address.address import get_company_address
 from erpnext.controllers.selling_controller import SellingController
-from frappe.automation.doctype.auto_repeat.auto_repeat import get_next_schedule_date
 from erpnext.selling.doctype.customer.customer import check_credit_limit
 from erpnext.stock.doctype.item.item import get_item_defaults
 from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
@@ -159,7 +158,6 @@
 					frappe.throw(_("Quotation {0} is cancelled").format(quotation))
 
 				doc.set_status(update=True)
-				doc.update_opportunity()
 
 	def validate_drop_ship(self):
 		for d in self.get('items'):
@@ -182,6 +180,7 @@
 			update_coupon_code_count(self.coupon_code,'used')
 
 	def on_cancel(self):
+		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
 		super(SalesOrder, self).on_cancel()
 
 		# Cannot cancel closed SO
@@ -418,8 +417,7 @@
 	def on_recurring(self, reference_doc, auto_repeat_doc):
 
 		def _get_delivery_date(ref_doc_delivery_date, red_doc_transaction_date, transaction_date):
-			delivery_date = get_next_schedule_date(ref_doc_delivery_date,
-				auto_repeat_doc.frequency, auto_repeat_doc.start_date, cint(auto_repeat_doc.repeat_on_day))
+			delivery_date = auto_repeat_doc.get_next_schedule_date(schedule_date=ref_doc_delivery_date)
 
 			if delivery_date <= transaction_date:
 				delivery_date_diff = frappe.utils.date_diff(ref_doc_delivery_date, red_doc_transaction_date)
@@ -832,56 +830,49 @@
 		frappe.throw(_("Please set a Supplier against the Items to be considered in the Purchase Order."))
 
 	for supplier in suppliers:
-		po = frappe.get_list("Purchase Order", filters={"sales_order":source_name, "supplier":supplier, "docstatus": ("<", "2")})
-		if len(po) == 0:
-			doc = get_mapped_doc("Sales Order", source_name, {
-				"Sales Order": {
-					"doctype": "Purchase Order",
-					"field_no_map": [
-						"address_display",
-						"contact_display",
-						"contact_mobile",
-						"contact_email",
-						"contact_person",
-						"taxes_and_charges",
-						"shipping_address",
-						"terms"
-					],
-					"validation": {
-						"docstatus": ["=", 1]
-					}
-				},
-				"Sales Order Item": {
-					"doctype": "Purchase Order Item",
-					"field_map":  [
-						["name", "sales_order_item"],
-						["parent", "sales_order"],
-						["stock_uom", "stock_uom"],
-						["uom", "uom"],
-						["conversion_factor", "conversion_factor"],
-						["delivery_date", "schedule_date"]
-			 		],
-					"field_no_map": [
-						"rate",
-						"price_list_rate",
-						"item_tax_template",
-						"discount_percentage",
-						"discount_amount",
-						"pricing_rules"
-					],
-					"postprocess": update_item,
-					"condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.supplier == supplier and doc.item_code in items_to_map
+		doc = get_mapped_doc("Sales Order", source_name, {
+			"Sales Order": {
+				"doctype": "Purchase Order",
+				"field_no_map": [
+					"address_display",
+					"contact_display",
+					"contact_mobile",
+					"contact_email",
+					"contact_person",
+					"taxes_and_charges",
+					"shipping_address",
+					"terms"
+				],
+				"validation": {
+					"docstatus": ["=", 1]
 				}
-			}, target_doc, set_missing_values)
+			},
+			"Sales Order Item": {
+				"doctype": "Purchase Order Item",
+				"field_map":  [
+					["name", "sales_order_item"],
+					["parent", "sales_order"],
+					["stock_uom", "stock_uom"],
+					["uom", "uom"],
+					["conversion_factor", "conversion_factor"],
+					["delivery_date", "schedule_date"]
+				],
+				"field_no_map": [
+					"rate",
+					"price_list_rate",
+					"item_tax_template",
+					"discount_percentage",
+					"discount_amount",
+					"pricing_rules"
+				],
+				"postprocess": update_item,
+				"condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.supplier == supplier and doc.item_code in items_to_map
+			}
+		}, target_doc, set_missing_values)
 
-			doc.insert()
-		else:
-			suppliers =[]
-	if suppliers:
+		doc.insert()
 		frappe.db.commit()
 		return doc
-	else:
-		frappe.msgprint(_("Purchase Order already created for all Sales Order items"))
 
 @frappe.whitelist()
 def make_purchase_order(source_name, selected_items=None, target_doc=None):
@@ -1096,4 +1087,4 @@
 
 	if not total_produced_qty and frappe.flags.in_patch: return
 
-	frappe.db.set_value('Sales Order Item', sales_order_item, 'produced_qty', total_produced_qty)
\ No newline at end of file
+	frappe.db.set_value('Sales Order Item', sales_order_item, 'produced_qty', total_produced_qty)
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 643e7cf..52a0174 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -17,6 +17,18 @@
 from erpnext.stock.doctype.item.test_item import make_item
 
 class TestSalesOrder(unittest.TestCase):
+
+	@classmethod
+	def setUpClass(cls):
+		cls.unlink_setting = int(frappe.db.get_value("Accounts Settings", "Accounts Settings",
+			"unlink_advance_payment_on_cancelation_of_order"))
+
+	@classmethod
+	def tearDownClass(cls) -> None:
+		# reset config to previous state
+		frappe.db.set_value("Accounts Settings", "Accounts Settings",
+			"unlink_advance_payment_on_cancelation_of_order", cls.unlink_setting)
+
 	def tearDown(self):
 		frappe.set_user("Administrator")
 
@@ -325,6 +337,9 @@
 		create_dn_against_so(so.name, 4)
 		make_sales_invoice(so.name)
 
+		prev_total = so.get("base_total")
+		prev_total_in_words = so.get("base_in_words")
+
 		first_item_of_so = so.get("items")[0]
 		trans_item = json.dumps([
 			{'item_code' : first_item_of_so.item_code, 'rate' : first_item_of_so.rate, \
@@ -340,6 +355,12 @@
 		self.assertEqual(so.get("items")[-1].amount, 1400)
 		self.assertEqual(so.status, 'To Deliver and Bill')
 
+		updated_total = so.get("base_total")
+		updated_total_in_words = so.get("base_in_words")
+
+		self.assertEqual(updated_total, prev_total+1400)
+		self.assertNotEqual(updated_total_in_words, prev_total_in_words)
+
 	def test_update_child_removing_item(self):
 		so = make_sales_order(**{
 			"item_list": [{
@@ -772,6 +793,59 @@
 		so.load_from_db()
 		so.cancel()
 
+	def test_drop_shipping_partial_order(self):
+		from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order_for_default_supplier, \
+			update_status as so_update_status
+
+		# make items
+		po_item1 = make_item("_Test Item for Drop Shipping 1", {"is_stock_item": 1, "delivered_by_supplier": 1})
+		po_item2 = make_item("_Test Item for Drop Shipping 2", {"is_stock_item": 1, "delivered_by_supplier": 1})
+
+		so_items = [
+			{
+				"item_code": po_item1.item_code,
+				"warehouse": "",
+				"qty": 2,
+				"rate": 400,
+				"delivered_by_supplier": 1,
+				"supplier": '_Test Supplier'
+			},
+			{
+				"item_code": po_item2.item_code,
+				"warehouse": "",
+				"qty": 2,
+				"rate": 400,
+				"delivered_by_supplier": 1,
+				"supplier": '_Test Supplier'
+			}
+		]
+
+		# create so and po
+		so = make_sales_order(item_list=so_items, do_not_submit=True)
+		so.submit()
+
+		# create po for only one item
+		po1 = make_purchase_order_for_default_supplier(so.name, selected_items=[so_items[0]])
+		po1.submit()
+
+		self.assertEqual(so.customer, po1.customer)
+		self.assertEqual(po1.items[0].sales_order, so.name)
+		self.assertEqual(po1.items[0].item_code, po_item1.item_code)
+		#test po item length
+		self.assertEqual(len(po1.items), 1)
+
+		# create po for remaining item
+		po2 = make_purchase_order_for_default_supplier(so.name, selected_items=[so_items[1]])
+		po2.submit()
+
+		# teardown
+		so_update_status("Draft", so.name)
+
+		po1.cancel()
+		po2.cancel()
+		so.load_from_db()
+		so.cancel()
+
 	def test_reserved_qty_for_closing_so(self):
 		bin = frappe.get_all("Bin", filters={"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"},
 			fields=["reserved_qty"])
@@ -987,6 +1061,38 @@
 
 		self.assertRaises(frappe.LinkExistsError, so_doc.cancel)
 
+	def test_cancel_sales_order_after_cancel_payment_entry(self):
+		from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
+		# make a sales order
+		so = make_sales_order()
+
+		# disable unlinking of payment entry
+		frappe.db.set_value("Accounts Settings", "Accounts Settings",
+			"unlink_advance_payment_on_cancelation_of_order", 0)
+
+		# create a payment entry against sales order
+		pe = get_payment_entry("Sales Order", so.name, bank_account="_Test Bank - _TC")
+		pe.reference_no = "1"
+		pe.reference_date = nowdate()
+		pe.paid_from_account_currency = so.currency
+		pe.paid_to_account_currency = so.currency
+		pe.source_exchange_rate = 1
+		pe.target_exchange_rate = 1
+		pe.paid_amount = so.grand_total
+		pe.save(ignore_permissions=True)
+		pe.submit()
+
+		# Cancel payment entry
+		po_doc = frappe.get_doc("Payment Entry", pe.name)
+		po_doc.cancel()
+
+		# Cancel sales order
+		try:
+			so_doc = frappe.get_doc('Sales Order', so.name)
+			so_doc.cancel()
+		except Exception:
+			self.fail("Can not cancel sales order with linked cancelled payment entry")
+
 	def test_request_for_raw_materials(self):
 		item = make_item("_Test Finished Item", {"is_stock_item": 1,
 			"maintain_stock": 1,
@@ -1145,4 +1251,4 @@
 	))
 	workflow.insert(ignore_permissions=True)
 
-	return workflow
\ No newline at end of file
+	return workflow
diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
index 159655b..37e47a9 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -46,6 +46,7 @@
   "base_rate",
   "base_amount",
   "pricing_rules",
+  "stock_uom_rate",
   "is_free_item",
   "section_break_24",
   "net_rate",
@@ -214,7 +215,6 @@
    "fieldtype": "Link",
    "label": "UOM",
    "options": "UOM",
-   "print_hide": 0,
    "reqd": 1
   },
   {
@@ -780,12 +780,20 @@
    "fieldname": "manufacturing_section_section",
    "fieldtype": "Section Break",
    "label": "Manufacturing Section"
+  },
+  {
+   "depends_on": "eval: doc.uom != doc.stock_uom",
+   "fieldname": "stock_uom_rate",
+   "fieldtype": "Currency",
+   "label": "Rate of Stock UOM",
+   "options": "currency",
+   "read_only": 1
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-012-07 20:54:32.309460",
+ "modified": "2021-01-30 21:35:07.617320",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order Item",
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index d4cde43..89fd9c7 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -69,6 +69,10 @@
 				dialog.fields_dict.balance_details.grid.refresh();
 			});
 		}
+		const pos_profile_query = {
+			query: 'erpnext.accounts.doctype.pos_profile.pos_profile.pos_profile_query',
+			filters: { company: frappe.defaults.get_default('company') }
+		}
 		const dialog = new frappe.ui.Dialog({
 			title: __('Create POS Opening Entry'),
 			static: true,
@@ -80,6 +84,7 @@
 				{
 					fieldtype: 'Link', label: __('POS Profile'),
 					options: 'POS Profile', fieldname: 'pos_profile', reqd: 1,
+					get_query: () => pos_profile_query,
 					onchange: () => fetch_pos_payment_methods()
 				},
 				{
@@ -101,6 +106,10 @@
 					})
 					return frappe.utils.play_sound("error");
 				}
+
+				// filter balance details for empty rows
+				balance_details = balance_details.filter(d => d.mode_of_payment);
+
 				const method = "erpnext.selling.page.point_of_sale.point_of_sale.create_opening_voucher";
 				const res = await frappe.call({ method, args: { pos_profile, company, balance_details }, freeze:true });
 				!res.exc && this.prepare_app_defaults(res.message);
@@ -124,9 +133,8 @@
 		});
 
 		frappe.db.get_doc("POS Profile", this.pos_profile).then((profile) => {
+			Object.assign(this.settings, profile);
 			this.settings.customer_groups = profile.customer_groups.map(group => group.customer_group);
-			this.settings.hide_images = profile.hide_images;
-			this.settings.auto_add_item_to_cart = profile.auto_add_item_to_cart;
 			this.make_app();
 		});
 	}
@@ -255,11 +263,9 @@
 				get_frm: () => this.frm,
 
 				cart_item_clicked: (item_code, batch_no, uom) => {
-					const item_row = this.frm.doc.items.find(
-						i => i.item_code === item_code 
-							&& i.uom === uom
-							&& (!batch_no || (batch_no && i.batch_no === batch_no))
-					);
+					const search_field = batch_no ? 'batch_no' : 'item_code';
+					const search_value = batch_no || item_code;
+					const item_row = this.frm.doc.items.find(i => i[search_field] === search_value && i.uom === uom);
 					this.item_details.toggle_item_details_section(item_row);
 				},
 
@@ -281,6 +287,7 @@
 	init_item_details() {
 		this.item_details = new erpnext.PointOfSale.ItemDetails({
 			wrapper: this.$components_wrapper,
+			settings: this.settings,
 			events: {
 				get_frm: () => this.frm,
 
@@ -415,6 +422,11 @@
 						() => this.item_selector.toggle_component(true)
 					]);
 				},
+				delete_order: (name) => {
+					frappe.model.delete_doc(this.frm.doc.doctype, name, () => {
+						this.recent_order_list.refresh_list();
+					});
+				},
 				new_order: () => {
 					frappe.run_serially([
 						() => frappe.dom.freeze(),
@@ -696,14 +708,14 @@
 		frappe.dom.freeze();
 		const { doctype, name, current_item } = this.item_details;
 
-		frappe.model.set_value(doctype, name, 'qty', 0);
-
-		this.frm.script_manager.trigger('qty', doctype, name).then(() => {
-			frappe.model.clear_doc(doctype, name);
-			this.update_cart_html(current_item, true);
-			this.item_details.toggle_item_details_section(undefined);
-			frappe.dom.unfreeze();
-		})
+		frappe.model.set_value(doctype, name, 'qty', 0)
+			.then(() => {
+				frappe.model.clear_doc(doctype, name);
+				this.update_cart_html(current_item, true);
+				this.item_details.toggle_item_details_section(undefined);
+				frappe.dom.unfreeze();
+			})
+			.catch(e => console.log(e));
 	}
 }
 
diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js
index 3938300..de70f16 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -5,6 +5,8 @@
 		this.customer_info = undefined;
 		this.hide_images = settings.hide_images;
 		this.allowed_customer_groups = settings.customer_groups;
+		this.allow_rate_change = settings.allow_rate_change;
+		this.allow_discount_change = settings.allow_discount_change;
 		
 		this.init_component();
 	}
@@ -201,7 +203,7 @@
 			me.events.checkout();
 			me.toggle_checkout_btn(false);
 
-			me.$add_discount_elem.removeClass("d-none");
+			me.allow_discount_change && me.$add_discount_elem.removeClass("d-none");
 		});
 
 		this.$totals_section.on('click', '.edit-cart-btn', () => {
@@ -479,11 +481,15 @@
 	update_totals_section(frm) {
 		if (!frm) frm = this.events.get_frm();
 
-		this.render_net_total(frm.doc.base_net_total);
-		this.render_grand_total(frm.doc.base_grand_total);
+		this.render_net_total(frm.doc.net_total);
+		this.render_grand_total(frm.doc.grand_total);
 
-		const taxes = frm.doc.taxes.map(t => { return { description: t.description, rate: t.rate }})
-		this.render_taxes(frm.doc.base_total_taxes_and_charges, taxes);
+		const taxes = frm.doc.taxes.map(t => {
+			return {
+				description: t.description, rate: t.rate
+			};
+		});
+		this.render_taxes(frm.doc.total_taxes_and_charges, taxes);
 	}
 	
 	render_net_total(value) {
@@ -545,7 +551,7 @@
 	get_cart_item({ item_code, batch_no, uom }) {
 		const batch_attr = `[data-batch-no="${escape(batch_no)}"]`;
 		const item_code_attr = `[data-item-code="${escape(item_code)}"]`;
-		const uom_attr = `[data-uom=${escape(uom)}]`;
+		const uom_attr = `[data-uom="${escape(uom)}"]`;
 
 		const item_selector = batch_no ? 
 			`.cart-item-wrapper${batch_attr}${uom_attr}` : `.cart-item-wrapper${item_code_attr}${uom_attr}`;
@@ -667,7 +673,7 @@
 	
 	update_selector_value_in_cart_item(selector, value, item) {
 		const $item_to_update = this.get_cart_item(item);
-		$item_to_update.attr(`data-${selector}`, value);
+		$item_to_update.attr(`data-${selector}`, escape(value));
 	}
 
 	toggle_checkout_btn(show_checkout) {
@@ -702,14 +708,26 @@
 	on_numpad_event($btn) {
 		const current_action = $btn.attr('data-button-value');
 		const action_is_field_edit = ['qty', 'discount_percentage', 'rate'].includes(current_action);
-
-		this.highlight_numpad_btn($btn, current_action);
+		const action_is_allowed = action_is_field_edit ? (
+			(current_action == 'rate' && this.allow_rate_change) ||
+			(current_action == 'discount_percentage' && this.allow_discount_change) ||
+			(current_action == 'qty')) : true;
 
 		const action_is_pressed_twice = this.prev_action === current_action;
 		const first_click_event = !this.prev_action;
 		const field_to_edit_changed = this.prev_action && this.prev_action != current_action;
 
 		if (action_is_field_edit) {
+			if (!action_is_allowed) {
+				const label = current_action == 'rate' ? 'Rate'.bold() : 'Discount'.bold();
+				const message = __('Editing {0} is not allowed as per POS Profile settings', [label]);
+				frappe.show_alert({
+					indicator: 'red',
+					message: message
+				});
+				frappe.utils.play_sound("error");
+				return;
+			}
 
 			if (first_click_event || field_to_edit_changed) {
 				this.prev_action = current_action;
@@ -753,6 +771,7 @@
 			this.numpad_value = current_action;
 		}
 
+		this.highlight_numpad_btn($btn, current_action);
 		this.events.numpad_event(this.numpad_value, this.prev_action);
 	}
 	
diff --git a/erpnext/selling/page/point_of_sale/pos_item_details.js b/erpnext/selling/page/point_of_sale/pos_item_details.js
index a4de9f1..259631d 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_details.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_details.js
@@ -1,7 +1,9 @@
 erpnext.PointOfSale.ItemDetails = class {
-	constructor({ wrapper, events }) {
+	constructor({ wrapper, events, settings }) {
 		this.wrapper = wrapper;
 		this.events = events;
+		this.allow_rate_change = settings.allow_rate_change;
+		this.allow_discount_change = settings.allow_discount_change;
 		this.current_item = {};
 
 		this.init_component();
@@ -207,17 +209,27 @@
 	bind_custom_control_change_event() {
 		const me = this;
 		if (this.rate_control) {
-			this.rate_control.df.onchange = function() {
-				if (this.value || flt(this.value) === 0) {
-					me.events.form_updated(me.doctype, me.name, 'rate', this.value).then(() => {
-						const item_row = frappe.get_doc(me.doctype, me.name);
-						const doc = me.events.get_frm().doc;
-
-						me.$item_price.html(format_currency(item_row.rate, doc.currency));
-						me.render_discount_dom(item_row);
-					});
-				}
+			if (this.allow_rate_change) {
+				this.rate_control.df.onchange = function() {
+					if (this.value || flt(this.value) === 0) {
+						me.events.form_updated(me.doctype, me.name, 'rate', this.value).then(() => {
+							const item_row = frappe.get_doc(me.doctype, me.name);
+							const doc = me.events.get_frm().doc;
+	
+							me.$item_price.html(format_currency(item_row.rate, doc.currency));
+							me.render_discount_dom(item_row);
+						});
+					}
+				};
+			} else {
+				this.rate_control.df.read_only = 1;
 			}
+			this.rate_control.refresh();
+		}
+
+		if (this.discount_percentage_control && !this.allow_discount_change) {
+			this.discount_percentage_control.df.read_only = 1;
+			this.discount_percentage_control.refresh();
 		}
 
 		if (this.warehouse_control) {
@@ -294,8 +306,16 @@
 		}
 
 		frappe.model.on("POS Invoice Item", "*", (fieldname, value, item_row) => {
+			const { item_code, batch_no, uom } = this.current_item; 
+			const item_code_is_same = item_code === item_row.item_code;
+			const batch_is_same = batch_no == item_row.batch_no;
+			const uom_is_same = uom === item_row.uom;
+			// check if current_item is same as item_row
+			const item_is_same = item_code_is_same && batch_is_same && uom_is_same ? true : false;
+
 			const field_control = me[`${fieldname}_control`];
-			if (field_control) {
+			
+			if (item_is_same && field_control && field_control.get_value() !== value) {
 				field_control.set_value(value);
 				cur_pos.update_cart_html(item_row);
 			}
diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js
index 6fd4c26..598f50f 100644
--- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js
+++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js
@@ -265,6 +265,14 @@
 			this.$summary_wrapper.addClass('d-none');
 		});
 
+		this.$summary_container.on('click', '.delete-btn', () => {
+			this.events.delete_order(this.doc.name);
+			this.show_summary_placeholder();
+			// this.toggle_component(false);
+			// this.$component.find('.no-summary-placeholder').removeClass('d-none');
+			// this.$summary_wrapper.addClass('d-none');
+		});
+
 		this.$summary_container.on('click', '.new-btn', () => {
 			this.events.new_order();
 			this.toggle_component(false);
@@ -401,7 +409,7 @@
 			return [{ condition: true, visible_btns: ['Print Receipt', 'Email Receipt', 'New Order'] }];
 
 		return [
-			{ condition: this.doc.docstatus === 0, visible_btns: ['Edit Order'] },
+			{ condition: this.doc.docstatus === 0, visible_btns: ['Edit Order', 'Delete Order'] },
 			{ condition: !this.doc.is_return && this.doc.docstatus === 1, visible_btns: ['Print Receipt', 'Email Receipt', 'Return']},
 			{ condition: this.doc.is_return && this.doc.docstatus === 1, visible_btns: ['Print Receipt', 'Email Receipt']}
 		];
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index e4d8965..e150271 100644
--- a/erpnext/selling/page/point_of_sale/pos_payment.js
+++ b/erpnext/selling/page/point_of_sale/pos_payment.js
@@ -168,30 +168,22 @@
 				me.toggle_numpad(true);
 
 				me.selected_mode = me[`${mode}_control`];
-				const doc = me.events.get_frm().doc;
-				me.selected_mode?.$input?.get(0).focus();
-				const current_value = me.selected_mode?.get_value()
-				!current_value && doc.grand_total > doc.paid_amount ? me.selected_mode?.set_value(doc.grand_total - doc.paid_amount) : '';
+				me.selected_mode && me.selected_mode.$input.get(0).focus();
+				me.auto_set_remaining_amount();
 			}
 		})
 
-		frappe.realtime.on("process_phone_payment", function(data) {
-			frappe.dom.unfreeze();
-			cur_frm.reload_doc();
-			let message = data["ResultDesc"];
-			let title = __("Payment Failed");
+		frappe.ui.form.on('POS Invoice', 'contact_mobile', (frm) => {
+			const contact = frm.doc.contact_mobile;
+			const request_button = $(this.request_for_payment_field.$input[0]);
+			if (contact) {
+				request_button.removeClass('btn-default').addClass('btn-primary');
+			} else {
+				request_button.removeClass('btn-primary').addClass('btn-default');
+      		}
+    	});
 
-			if (data["ResultCode"] == 0) {
-				title = __("Payment Received");
-				$('.btn.btn-xs.btn-default[data-fieldname=request_for_payment]').html(`Payment Received`)
-				me.events.submit_invoice();
-			}
-
-			frappe.msgprint({
-				"message": message,
-				"title": title
-			});
-		});
+		this.setup_listener_for_payments();
 
 		this.$payment_modes.on('click', '.shortcut', function(e) {
 			const value = $(this).attr('data-value');
@@ -250,6 +242,41 @@
 		})
 	}
 
+	setup_listener_for_payments() {
+		frappe.realtime.on("process_phone_payment", (data) => {
+			const doc = this.events.get_frm().doc;
+			const { response, amount, success, failure_message } = data;
+			let message, title;
+
+			if (success) {
+				title = __("Payment Received");
+				if (amount >= doc.grand_total) {
+					frappe.dom.unfreeze();
+					message = __("Payment of {0} received successfully.", [format_currency(amount, doc.currency, 0)]);
+					this.events.submit_invoice();
+					cur_frm.reload_doc();
+
+				} else {
+					message = __("Payment of {0} received successfully. Waiting for other requests to complete...", [format_currency(amount, doc.currency, 0)]);
+				}
+			} else if (failure_message) {
+				message = failure_message;
+				title = __("Payment Failed");
+			}
+
+			frappe.msgprint({ "message": message, "title": title });
+		});
+	}
+
+	auto_set_remaining_amount() {
+		const doc = this.events.get_frm().doc;
+		const remaining_amount = doc.grand_total - doc.paid_amount;
+		const current_value = this.selected_mode ? this.selected_mode.get_value() : undefined;
+		if (!current_value && remaining_amount > 0 && this.selected_mode) {
+			this.selected_mode.set_value(remaining_amount);
+		}
+	}
+
 	attach_shortcuts() {
 		const ctrl_label = frappe.utils.is_mac() ? '⌘' : 'Ctrl';
 		this.$component.find('.submit-order').attr("title", `${ctrl_label}+Enter`);
@@ -370,9 +397,11 @@
 					fieldtype: 'Currency',
 					placeholder: __('Enter {0} amount.', [p.mode_of_payment]),
 					onchange: function() {
-						if (this.value || this.value == 0) {
-							frappe.model.set_value(p.doctype, p.name, 'amount', flt(this.value))
-								.then(() => me.update_totals_section());
+						const current_value = frappe.model.get_value(p.doctype, p.name, 'amount');
+						if (current_value != this.value) {
+							frappe.model
+								.set_value(p.doctype, p.name, 'amount', flt(this.value))
+								.then(() => me.update_totals_section())
 
 							const formatted_currency = format_currency(this.value, currency);
 							me.$payment_modes.find(`.${mode}-amount`).html(formatted_currency);
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.js b/erpnext/selling/report/sales_analytics/sales_analytics.js
index 0e565a3..9089b53 100644
--- a/erpnext/selling/report/sales_analytics/sales_analytics.js
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.js
@@ -74,67 +74,71 @@
 		return Object.assign(options, {
 			checkboxColumn: true,
 			events: {
-				onCheckRow: function(data) {
+				onCheckRow: function (data) {
+					if (!data) return;
+					const data_doctype = $(
+						data[2].html
+					)[0].attributes.getNamedItem("data-doctype").value;
+					const tree_type = frappe.query_report.filters[0].value;
+					if (data_doctype != tree_type) return;
+
 					row_name = data[2].content;
 					length = data.length;
 
-					var tree_type = frappe.query_report.filters[0].value;
-
-					if(tree_type == "Customer") {
-						row_values = data.slice(4,length-1).map(function (column) {
-							return column.content;
-						})
+					if (tree_type == "Customer") {
+						row_values = data
+							.slice(4, length - 1)
+							.map(function (column) {
+								return column.content;
+							});
 					} else if (tree_type == "Item") {
-						row_values = data.slice(5,length-1).map(function (column) {
-							return column.content;
-						})
-					}
-					else {
-						row_values = data.slice(3,length-1).map(function (column) {
-							return column.content;
-						})
+						row_values = data
+							.slice(5, length - 1)
+							.map(function (column) {
+								return column.content;
+							});
+					} else {
+						row_values = data
+							.slice(3, length - 1)
+							.map(function (column) {
+								return column.content;
+							});
 					}
 
 					entry = {
-						'name':row_name,
-						'values':row_values
-					}
+						name: row_name,
+						values: row_values,
+					};
 
 					let raw_data = frappe.query_report.chart.data;
 					let new_datasets = raw_data.datasets;
 
-					var found = false;
-
-					for(var i=0; i < new_datasets.length;i++){
-						if(new_datasets[i].name == row_name){
-							found = true;
-							new_datasets.splice(i,1);
-							break;
+					let element_found = new_datasets.some((element, index, array)=>{
+						if(element.name == row_name){
+							array.splice(index, 1)
+							return true
 						}
-					}
+						return false
+					})
 
-					if(!found){
+					if (!element_found) {
 						new_datasets.push(entry);
 					}
 
 					let new_data = {
 						labels: raw_data.labels,
-						datasets: new_datasets
-					}
-
-					setTimeout(() => {
-						frappe.query_report.chart.update(new_data)
-					}, 500)
-
-
-					setTimeout(() => {
-						frappe.query_report.chart.draw(true);
-					}, 1000)
+						datasets: new_datasets,
+					};
+					chart_options = {
+						data: new_data,
+						type: "line",
+					};
+					frappe.query_report.render_chart(chart_options);
 
 					frappe.query_report.raw_chart_data = new_data;
 				},
-			}
-		})
+			},
+		});
 	},
 }
 
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index 7f00fca..ce08464 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -399,6 +399,10 @@
 			}
 	},
 
+	batch_no: function(doc, cdt, cdn) {
+		this._super(doc, cdt, cdn);
+	},
+
 	qty: function(doc, cdt, cdn) {
 		this._super(doc, cdt, cdn);
 
diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py
index 566f20c..7a72fe3 100644
--- a/erpnext/setup/doctype/company/delete_company_transactions.py
+++ b/erpnext/setup/doctype/company/delete_company_transactions.py
@@ -28,7 +28,7 @@
 			"Party Account", "Employee", "Sales Taxes and Charges Template",
 			"Purchase Taxes and Charges Template", "POS Profile", "BOM",
 			"Company", "Bank Account", "Item Tax Template", "Mode Of Payment",
-			"Item Default"):
+			"Item Default", "Customer", "Supplier"):
 				delete_for_doctype(doctype, company_name)
 
 	# reset company values
diff --git a/erpnext/setup/doctype/customer_group/customer_group.json b/erpnext/setup/doctype/customer_group/customer_group.json
index 10f9bd0..0e2ed9e 100644
--- a/erpnext/setup/doctype/customer_group/customer_group.json
+++ b/erpnext/setup/doctype/customer_group/customer_group.json
@@ -139,7 +139,7 @@
  "idx": 1,
  "is_tree": 1,
  "links": [],
- "modified": "2020-03-18 18:10:13.048492",
+ "modified": "2021-02-08 17:01:52.162202",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Customer Group",
@@ -189,6 +189,15 @@
    "permlevel": 1,
    "read": 1,
    "role": "Sales Manager"
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "report": 1,
+   "role": "Customer",
+   "select": 1,
+   "share": 1
   }
  ],
  "search_fields": "parent_customer_group",
diff --git a/erpnext/setup/doctype/item_group/item_group.json b/erpnext/setup/doctype/item_group/item_group.json
index 004421d..fc464b2 100644
--- a/erpnext/setup/doctype/item_group/item_group.json
+++ b/erpnext/setup/doctype/item_group/item_group.json
@@ -245,6 +245,15 @@
    "read": 1,
    "report": 1,
    "role": "Accounts User"
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "report": 1,
+   "role": "Customer",
+   "select": 1,
+   "share": 1
   }
  ],
  "search_fields": "parent_item_group",
diff --git a/erpnext/setup/doctype/territory/territory.json b/erpnext/setup/doctype/territory/territory.json
index aa8e048..a25bda0 100644
--- a/erpnext/setup/doctype/territory/territory.json
+++ b/erpnext/setup/doctype/territory/territory.json
@@ -123,7 +123,7 @@
  "idx": 1,
  "is_tree": 1,
  "links": [],
- "modified": "2020-03-18 18:11:36.623555",
+ "modified": "2021-02-08 17:10:03.767426",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Territory",
@@ -166,6 +166,15 @@
   {
    "read": 1,
    "role": "Maintenance User"
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "report": 1,
+   "role": "Customer",
+   "select": 1,
+   "share": 1
   }
  ],
  "search_fields": "parent_territory,territory_manager",
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index 72ed002..5053c6a 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -195,6 +195,7 @@
 		{'doctype': "Party Type", "party_type": "Member", "account_type": "Receivable"},
 		{'doctype': "Party Type", "party_type": "Shareholder", "account_type": "Payable"},
 		{'doctype': "Party Type", "party_type": "Student", "account_type": "Receivable"},
+		{'doctype': "Party Type", "party_type": "Donor", "account_type": "Receivable"},
 
 		{'doctype': "Opportunity Type", "name": "Hub"},
 		{'doctype': "Opportunity Type", "name": _("Sales")},
diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py
index 21ecaa1..9f50aba 100644
--- a/erpnext/shopping_cart/cart.py
+++ b/erpnext/shopping_cart/cart.py
@@ -178,6 +178,13 @@
 	lead_doc.update(lead)
 	lead_doc.set('lead_owner', '')
 
+	if not frappe.db.exists('Lead Source', 'Product Inquiry'):
+		frappe.get_doc({
+			'doctype': 'Lead Source',
+			'source_name' : 'Product Inquiry'
+		}).insert(ignore_permissions=True)
+	lead_doc.set('source', 'Product Inquiry')
+
 	try:
 		lead_doc.save(ignore_permissions=True)
 	except frappe.exceptions.DuplicateEntryError:
diff --git a/erpnext/stock/__init__.py b/erpnext/stock/__init__.py
index 8d64efe..283f7d5 100644
--- a/erpnext/stock/__init__.py
+++ b/erpnext/stock/__init__.py
@@ -38,7 +38,7 @@
 			frappe.flags.warehouse_account_map[company] = warehouse_account
 		else:
 			frappe.flags.warehouse_account_map = warehouse_account
-	
+
 	return frappe.flags.warehouse_account_map.get(company) or frappe.flags.warehouse_account_map
 
 def get_warehouse_account(warehouse, warehouse_account=None):
@@ -65,9 +65,13 @@
 		account = get_company_default_inventory_account(warehouse.company)
 
 	if not account and warehouse.company:
+		account = frappe.db.get_value('Account',
+			{'account_type': 'Stock', 'is_group': 0, 'company': warehouse.company}, 'name')
+
+	if not account and warehouse.company and not warehouse.is_group:
 		frappe.throw(_("Please set Account in Warehouse {0} or Default Inventory Account in Company {1}")
 			.format(warehouse.name, warehouse.company))
 	return account
 
 def get_company_default_inventory_account(company):
-	return frappe.get_cached_value('Company',  company,  'default_inventory_account')
+	return frappe.get_cached_value('Company',  company, 'default_inventory_account')
diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js
index 9bd03d4..d3c442d 100644
--- a/erpnext/stock/dashboard/item_dashboard.js
+++ b/erpnext/stock/dashboard/item_dashboard.js
@@ -24,6 +24,16 @@
 			handle_move_add($(this), "Add")
 		});
 
+		this.content.on('click', '.btn-edit', function() {
+			let item = unescape($(this).attr('data-item'));
+			let warehouse = unescape($(this).attr('data-warehouse'));
+			let company = unescape($(this).attr('data-company'));
+			frappe.db.get_value('Putaway Rule',
+				{'item_code': item, 'warehouse': warehouse, 'company': company}, 'name', (r) => {
+					frappe.set_route("Form", "Putaway Rule", r.name);
+				});
+		});
+
 		function handle_move_add(element, action) {
 			let item = unescape(element.attr('data-item'));
 			let warehouse = unescape(element.attr('data-warehouse'));
@@ -59,7 +69,7 @@
 
 		// more
 		this.content.find('.btn-more').on('click', function() {
-			me.start += 20;
+			me.start += me.page_length;
 			me.refresh();
 		});
 
@@ -69,33 +79,43 @@
 			this.before_refresh();
 		}
 
+		let args = {
+			item_code: this.item_code,
+			warehouse: this.warehouse,
+			parent_warehouse: this.parent_warehouse,
+			item_group: this.item_group,
+			company: this.company,
+			start: this.start,
+			sort_by: this.sort_by,
+			sort_order: this.sort_order
+		};
+
 		var me = this;
 		frappe.call({
-			method: 'erpnext.stock.dashboard.item_dashboard.get_data',
-			args: {
-				item_code: this.item_code,
-				warehouse: this.warehouse,
-				item_group: this.item_group,
-				start: this.start,
-				sort_by: this.sort_by,
-				sort_order: this.sort_order,
-			},
+			method: this.method,
+			args: args,
 			callback: function(r) {
 				me.render(r.message);
 			}
 		});
 	},
 	render: function(data) {
-		if(this.start===0) {
+		if (this.start===0) {
 			this.max_count = 0;
 			this.result.empty();
 		}
 
-		var context = this.get_item_dashboard_data(data, this.max_count, true);
+		let context = "";
+		if (this.page_name === "warehouse-capacity-summary") {
+			context = this.get_capacity_dashboard_data(data);
+		} else {
+			context = this.get_item_dashboard_data(data, this.max_count, true);
+		}
+
 		this.max_count = this.max_count;
 
 		// show more button
-		if(data && data.length===21) {
+		if (data && data.length===(this.page_length + 1)) {
 			this.content.find('.more').removeClass('hidden');
 
 			// remove the last element
@@ -106,12 +126,17 @@
 
 		// If not any stock in any warehouses provide a message to end user
 		if (context.data.length > 0) {
-			$(frappe.render_template('item_dashboard_list', context)).appendTo(this.result);
+			this.content.find('.result').css('text-align', 'unset');
+			$(frappe.render_template(this.template, context)).appendTo(this.result);
 		} else {
-			var message = __("Currently no stock available in any warehouse");
-			$(`<span class='text-muted small'>  ${message} </span>`).appendTo(this.result);
+			var message = __("No Stock Available Currently");
+			this.content.find('.result').css('text-align', 'center');
+
+			$(`<div class='text-muted' style='margin: 20px 5px; font-weight: lighter;'>
+				${message} </div>`).appendTo(this.result);
 		}
 	},
+
 	get_item_dashboard_data: function(data, max_count, show_item) {
 		if(!max_count) max_count = 0;
 		if(!data) data = [];
@@ -128,8 +153,8 @@
 				d.total_reserved, max_count);
 		});
 
-		var can_write = 0;
-		if(frappe.boot.user.can_write.indexOf("Stock Entry")>=0){
+		let can_write = 0;
+		if (frappe.boot.user.can_write.indexOf("Stock Entry") >= 0) {
 			can_write = 1;
 		}
 
@@ -138,9 +163,27 @@
 			max_count: max_count,
 			can_write:can_write,
 			show_item: show_item || false
+		};
+	},
+
+	get_capacity_dashboard_data: function(data) {
+		if (!data) data = [];
+
+		data.forEach(function(d) {
+			d.color =  d.percent_occupied >=80 ? "#f8814f" : "#2490ef";
+		});
+
+		let can_write = 0;
+		if (frappe.boot.user.can_write.indexOf("Putaway Rule") >= 0) {
+			can_write = 1;
 		}
+
+		return {
+			data: data,
+			can_write: can_write,
+		};
 	}
-})
+});
 
 erpnext.stock.move_item = function(item, source, target, actual_qty, rate, callback) {
 	var dialog = new frappe.ui.Dialog({
diff --git a/erpnext/stock/dashboard/warehouse_capacity_dashboard.py b/erpnext/stock/dashboard/warehouse_capacity_dashboard.py
new file mode 100644
index 0000000..ab573e5
--- /dev/null
+++ b/erpnext/stock/dashboard/warehouse_capacity_dashboard.py
@@ -0,0 +1,69 @@
+from __future__ import unicode_literals
+
+import frappe
+from frappe.model.db_query import DatabaseQuery
+from frappe.utils import nowdate
+from frappe.utils import flt
+from erpnext.stock.utils import get_stock_balance
+
+@frappe.whitelist()
+def get_data(item_code=None, warehouse=None, parent_warehouse=None,
+	company=None, start=0, sort_by="stock_capacity", sort_order="desc"):
+	"""Return data to render the warehouse capacity dashboard."""
+	filters = get_filters(item_code, warehouse, parent_warehouse, company)
+
+	no_permission, filters = get_warehouse_filter_based_on_permissions(filters)
+	if no_permission:
+		return []
+
+	capacity_data = get_warehouse_capacity_data(filters, start)
+
+	asc_desc = -1 if sort_order == "desc" else 1
+	capacity_data = sorted(capacity_data, key = lambda i: (i[sort_by] * asc_desc))
+
+	return capacity_data
+
+def get_filters(item_code=None, warehouse=None, parent_warehouse=None,
+	company=None):
+	filters = [['disable', '=', 0]]
+	if item_code:
+		filters.append(['item_code', '=', item_code])
+	if warehouse:
+		filters.append(['warehouse', '=', warehouse])
+	if company:
+		filters.append(['company', '=', company])
+	if parent_warehouse:
+		lft, rgt = frappe.db.get_value("Warehouse", parent_warehouse, ["lft", "rgt"])
+		warehouses = frappe.db.sql_list("""
+			select name from `tabWarehouse`
+			where lft >=%s and rgt<=%s
+		""", (lft, rgt))
+		filters.append(['warehouse', 'in', warehouses])
+	return filters
+
+def get_warehouse_filter_based_on_permissions(filters):
+	try:
+		# check if user has any restrictions based on user permissions on warehouse
+		if DatabaseQuery('Warehouse', user=frappe.session.user).build_match_conditions():
+			filters.append(['warehouse', 'in', [w.name for w in frappe.get_list('Warehouse')]])
+		return False, filters
+	except frappe.PermissionError:
+		# user does not have access on warehouse
+		return True, []
+
+def get_warehouse_capacity_data(filters, start):
+	capacity_data = frappe.db.get_all('Putaway Rule',
+		fields=['item_code', 'warehouse','stock_capacity', 'company'],
+		filters=filters,
+		limit_start=start,
+		limit_page_length='11'
+	)
+
+	for entry in capacity_data:
+		balance_qty = get_stock_balance(entry.item_code, entry.warehouse, nowdate()) or 0
+		entry.update({
+			'actual_qty': balance_qty,
+			'percent_occupied': flt((flt(balance_qty) / flt(entry.stock_capacity)) * 100, 0)
+		})
+
+	return capacity_data
\ No newline at end of file
diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json
index 9068e33..74cc42d 100644
--- a/erpnext/stock/desk_page/stock/stock.json
+++ b/erpnext/stock/desk_page/stock/stock.json
@@ -8,12 +8,12 @@
   {
    "hidden": 0,
    "label": "Stock Transactions",
-   "links": "[\n     {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Material Request\",\n        \"name\": \"Material Request\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Stock Entry\",\n        \"name\": \"Stock Entry\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\",\n            \"Customer\"\n        ],\n        \"label\": \"Delivery Note\",\n        \"name\": \"Delivery Note\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\",\n            \"Supplier\"\n        ],\n        \"label\": \"Purchase Receipt\",\n        \"name\": \"Purchase Receipt\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Pick List\",\n        \"name\": \"Pick List\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Shipment\",\n        \"name\": \"Shipment\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Delivery Trip\",\n        \"name\": \"Delivery Trip\",\n        \"type\": \"doctype\"\n    }\n]"
+   "links": "[\n     {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Material Request\",\n        \"name\": \"Material Request\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Stock Entry\",\n        \"name\": \"Stock Entry\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\",\n            \"Customer\"\n        ],\n        \"label\": \"Delivery Note\",\n        \"name\": \"Delivery Note\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\",\n            \"Supplier\"\n        ],\n        \"label\": \"Purchase Receipt\",\n        \"name\": \"Purchase Receipt\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Pick List\",\n        \"name\": \"Pick List\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Putaway Rule\",\n        \"name\": \"Putaway Rule\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Shipment\",\n        \"name\": \"Shipment\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Delivery Trip\",\n        \"name\": \"Delivery Trip\",\n        \"type\": \"doctype\"\n    }\n]"
   },
   {
    "hidden": 0,
    "label": "Stock Reports",
-   "links": "[\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Stock Ledger Entry\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Ledger\",\n        \"name\": \"Stock Ledger\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Stock Ledger Entry\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Balance\",\n        \"name\": \"Stock Balance\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Item\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Projected Qty\",\n        \"name\": \"Stock Projected Qty\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Stock Summary\",\n        \"name\": \"stock-balance\",\n        \"type\": \"page\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Item\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Ageing\",\n        \"name\": \"Stock Ageing\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Item\",\n        \"is_query_report\": true,\n        \"label\": \"Item Price Stock\",\n        \"name\": \"Item Price Stock\",\n        \"type\": \"report\"\n    }\n]"
+   "links": "[\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Stock Ledger Entry\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Ledger\",\n        \"name\": \"Stock Ledger\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Stock Ledger Entry\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Balance\",\n        \"name\": \"Stock Balance\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Item\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Projected Qty\",\n        \"name\": \"Stock Projected Qty\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Stock Summary\",\n        \"name\": \"stock-balance\",\n        \"type\": \"page\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Item\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Ageing\",\n        \"name\": \"Stock Ageing\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Item\",\n        \"is_query_report\": true,\n        \"label\": \"Item Price Stock\",\n        \"name\": \"Item Price Stock\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\",\n            \"Putaway Rule\"\n        ],\n        \"label\": \"Warehouse Capacity Summary\",\n        \"name\": \"warehouse-capacity-summary\",\n        \"type\": \"page\"\n    }\n]"
   },
   {
    "hidden": 0,
@@ -58,7 +58,7 @@
  "idx": 0,
  "is_standard": 1,
  "label": "Stock",
- "modified": "2020-12-02 15:47:41.532942",
+ "modified": "2020-12-08 15:47:41.532942",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock",
diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py
index c8424f1..8fdda56 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -93,7 +93,7 @@
 
 			if create_new_batch:
 				if batch_number_series:
-					self.batch_id = make_autoname(batch_number_series)
+					self.batch_id = make_autoname(batch_number_series, doc=self)
 				elif batch_uses_naming_series():
 					self.batch_id = self.get_name_from_naming_series()
 				else:
diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py
index e41f1a8..cbd272d 100644
--- a/erpnext/stock/doctype/batch/test_batch.py
+++ b/erpnext/stock/doctype/batch/test_batch.py
@@ -8,6 +8,8 @@
 
 from erpnext.stock.doctype.batch.batch import get_batch_qty, UnableToSelectBatchError, get_batch_no
 from frappe.utils import cint, flt
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+from erpnext.stock.get_item_details import get_item_details
 
 class TestBatch(unittest.TestCase):
 	def test_item_has_batch_enabled(self):
@@ -182,7 +184,7 @@
 		stock_entry.cancel()
 		current_batch_qty = flt(frappe.db.get_value("Batch", "B100", "batch_qty"))
 		self.assertEqual(current_batch_qty, existing_batch_qty)
-		
+
 	@classmethod
 	def make_new_batch_and_entry(cls, item_name, batch_name, warehouse):
 		'''Make a new stock entry for given target warehouse and batch name of item'''
@@ -252,6 +254,72 @@
 
 		return batch
 
+	def test_batch_wise_item_price(self):
+		if not frappe.db.get_value('Item', '_Test Batch Price Item'):
+			frappe.get_doc({
+				'doctype': 'Item',
+				'is_stock_item': 1,
+				'item_code': '_Test Batch Price Item',
+				'item_group': 'Products',
+				'has_batch_no': 1,
+				'create_new_batch': 1
+			}).insert(ignore_permissions=True)
+
+		batch1 = create_batch('_Test Batch Price Item', 200, 1)
+		batch2 = create_batch('_Test Batch Price Item', 300, 1)
+		batch3 = create_batch('_Test Batch Price Item', 400, 0)
+
+		args = frappe._dict({
+			"item_code": "_Test Batch Price Item",
+			"company": "_Test Company with perpetual inventory",
+			"price_list": "_Test Price List",
+			"currency": "_Test Currency",
+			"doctype": "Sales Invoice",
+			"conversion_rate": 1,
+			"price_list_currency": "_Test Currency",
+			"plc_conversion_rate": 1,
+			"customer": "_Test Customer",
+			"name": None
+		})
+
+		#test price for batch1
+		args.update({'batch_no': batch1})
+		details = get_item_details(args)
+		self.assertEqual(details.get('price_list_rate'), 200)
+
+		#test price for batch2
+		args.update({'batch_no': batch2})
+		details = get_item_details(args)
+		self.assertEqual(details.get('price_list_rate'), 300)
+
+		#test price for batch3
+		args.update({'batch_no': batch3})
+		details = get_item_details(args)
+		self.assertEqual(details.get('price_list_rate'), 400)
+
+def create_batch(item_code, rate, create_item_price_for_batch):
+	pi = make_purchase_invoice(company="_Test Company",
+		warehouse= "Stores - _TC", cost_center = "Main - _TC", update_stock=1,
+		expense_account ="_Test Account Cost for Goods Sold - _TC", item_code=item_code)
+
+	batch = frappe.db.get_value('Batch', {'item': item_code, 'reference_name': pi.name})
+
+	if not create_item_price_for_batch:
+		create_price_list_for_batch(item_code, None, rate)
+	else:
+		create_price_list_for_batch(item_code, batch, rate)
+
+	return batch
+
+def create_price_list_for_batch(item_code, batch, rate):
+	frappe.get_doc({
+		'doctype': 'Item Price',
+		'item_code': '_Test Batch Price Item',
+		'price_list': '_Test Price List',
+		'batch_no': batch,
+		'price_list_rate': rate
+	}).insert()
+
 def make_new_batch(**args):
 	args = frappe._dict(args)
 
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index ab19b77..0514bd2 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -16,6 +16,7 @@
 	def update_stock(self, args, allow_negative_stock=False, via_landed_cost_voucher=False):
 		'''Called from erpnext.stock.utils.update_bin'''
 		self.update_qty(args)
+
 		if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
 			from erpnext.stock.stock_ledger import update_entries_after, update_qty_in_future_sle
 
@@ -34,11 +35,13 @@
 				"posting_time": args.get("posting_time"),
 				"voucher_type": args.get("voucher_type"),
 				"voucher_no": args.get("voucher_no"),
-				"sle_id": args.name
+				"sle_id": args.name,
+				"creation": args.creation
 			}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
 
-			# Update qty_after_transaction in future SLEs of this item and warehouse
-			update_qty_in_future_sle(args)
+			# update qty in future ale and Validate negative qty
+			update_qty_in_future_sle(args, allow_negative_stock)
+
 
 	def update_qty(self, args):
 		# update the stock values (for current quantities)
@@ -51,7 +54,7 @@
 		self.reserved_qty = flt(self.reserved_qty) + flt(args.get("reserved_qty"))
 		self.indented_qty = flt(self.indented_qty) + flt(args.get("indented_qty"))
 		self.planned_qty = flt(self.planned_qty) + flt(args.get("planned_qty"))
-
+		
 		self.set_projected_qty()
 		self.db_update()
 
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index 5f2658c..334bdea 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -7,13 +7,14 @@
 
 frappe.provide("erpnext.stock");
 frappe.provide("erpnext.stock.delivery_note");
+frappe.provide("erpnext.accounts.dimensions");
 
 frappe.ui.form.on("Delivery Note", {
 	setup: function(frm) {
 		frm.custom_make_buttons = {
 			'Packing Slip': 'Packing Slip',
 			'Installation Note': 'Installation Note',
-			'Sales Invoice': 'Invoice',
+			'Sales Invoice': 'Sales Invoice',
 			'Stock Entry': 'Return',
 			'Shipment': 'Shipment'
 		},
@@ -76,7 +77,7 @@
 			}
 		});
 
-
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
 	},
 
 	print_without_amount: function(frm) {
@@ -94,13 +95,19 @@
 			frm.page.set_inner_btn_group_as_primary(__('Create'));
 		}
 
-		if (frm.doc.docstatus === 1 && frm.doc.is_internal_customer && !frm.doc.inter_company_reference) {
-			frm.add_custom_button(__('Purchase Receipt'), function() {
-				frappe.model.open_mapped_doc({
-					method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt',
-					frm: frm,
-				})
-			}, __('Create'));
+		if (frm.doc.docstatus == 1 && !frm.doc.inter_company_reference) {
+			let internal = me.frm.doc.is_internal_customer;
+			if (internal) {
+				let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Receipt" :
+					"Inter Company Purchase Receipt";
+
+				me.frm.add_custom_button(button_label, function() {
+					frappe.model.open_mapped_doc({
+						method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt',
+						frm: frm,
+					});
+				}, __('Create'));
+			}
 		}
 	}
 });
@@ -296,15 +303,6 @@
 			}
 		})
 	},
-
-	to_warehouse: function() {
-		let packed_items_table = this.frm.doc["packed_items"];
-		this.autofill_warehouse(this.frm.doc["items"], "target_warehouse", this.frm.doc.to_warehouse);
-		if (packed_items_table && packed_items_table.length) {
-			this.autofill_warehouse(packed_items_table, "target_warehouse", this.frm.doc.to_warehouse);
-		}
-	}
-
 });
 
 $.extend(cur_frm.cscript, new erpnext.stock.DeliveryNoteController({frm: cur_frm}));
@@ -318,6 +316,7 @@
 
 	company: function(frm) {
 		frm.trigger("unhide_account_head");
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
 	},
 
 	unhide_account_head: function(frm) {
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index c9f8d08..f595aad 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -53,7 +53,7 @@
   "sec_warehouse",
   "set_warehouse",
   "col_break_warehouse",
-  "to_warehouse",
+  "set_target_warehouse",
   "items_section",
   "scan_barcode",
   "items",
@@ -117,6 +117,7 @@
   "source",
   "column_break5",
   "is_internal_customer",
+  "represents_company",
   "inter_company_reference",
   "per_billed",
   "customer_group",
@@ -503,18 +504,6 @@
    "fieldtype": "Column Break"
   },
   {
-   "description": "Required only for sample item.",
-   "fieldname": "to_warehouse",
-   "fieldtype": "Link",
-   "in_standard_filter": 1,
-   "label": "To Warehouse",
-   "no_copy": 1,
-   "oldfieldname": "to_warehouse",
-   "oldfieldtype": "Link",
-   "options": "Warehouse",
-   "print_hide": 1
-  },
-  {
    "fieldname": "items_section",
    "fieldtype": "Section Break",
    "oldfieldtype": "Section Break",
@@ -1261,13 +1250,34 @@
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "depends_on": "eval: doc.is_internal_customer",
+   "fieldname": "set_target_warehouse",
+   "fieldtype": "Link",
+   "in_standard_filter": 1,
+   "label": "Set Target Warehouse",
+   "no_copy": 1,
+   "oldfieldname": "to_warehouse",
+   "oldfieldtype": "Link",
+   "options": "Warehouse",
+   "print_hide": 1
+  },
+  {
+   "description": "Company which internal customer represents.",
+   "fetch_from": "customer.represents_company",
+   "fieldname": "represents_company",
+   "fieldtype": "Link",
+   "label": "Represents Company",
+   "options": "Company",
+   "read_only": 1
   }
  ],
  "icon": "fa fa-truck",
  "idx": 146,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-11-30 12:54:45.407289",
+ "modified": "2020-12-26 17:07:59.194403",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note",
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index a30cadf..fa5a7fb 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -664,7 +664,8 @@
 	return make_inter_company_transaction("Delivery Note", source_name, target_doc)
 
 def make_inter_company_transaction(doctype, source_name, target_doc=None):
-	from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_transaction, get_inter_company_details
+	from erpnext.accounts.doctype.sales_invoice.sales_invoice import (validate_inter_company_transaction,
+		get_inter_company_details, update_address, update_taxes, set_purchase_references)
 
 	if doctype == 'Delivery Note':
 		source_doc = frappe.get_doc(doctype, source_name)
@@ -682,6 +683,7 @@
 
 	def set_missing_values(source, target):
 		target.run_method("set_missing_values")
+		set_purchase_references(target)
 
 		if target.doctype == 'Purchase Receipt':
 			master_doctype = 'Purchase Taxes and Charges Template'
@@ -697,21 +699,35 @@
 		if target_doc.doctype == 'Purchase Receipt':
 			target_doc.company = details.get("company")
 			target_doc.supplier = details.get("party")
-			target_doc.supplier_address = source_doc.company_address
-			target_doc.shipping_address = source_doc.shipping_address_name or source_doc.customer_address
 			target_doc.buying_price_list = source_doc.selling_price_list
 			target_doc.is_internal_supplier = 1
 			target_doc.inter_company_reference = source_doc.name
+
+			# Invert the address on target doc creation
+			update_address(target_doc, 'supplier_address', 'address_display', source_doc.company_address)
+			update_address(target_doc, 'shipping_address', 'shipping_address_display', source_doc.customer_address)
+
+			update_taxes(target_doc, party=target_doc.supplier, party_type='Supplier', company=target_doc.company,
+				doctype=target_doc.doctype, party_address=target_doc.supplier_address,
+				company_address=target_doc.shipping_address)
 		else:
 			target_doc.company = details.get("company")
 			target_doc.customer = details.get("party")
 			target_doc.company_address = source_doc.supplier_address
-			target_doc.shipping_address_name = source_doc.shipping_address
 			target_doc.selling_price_list = source_doc.buying_price_list
 			target_doc.is_internal_customer = 1
 			target_doc.inter_company_reference = source_doc.name
 
-	doclist = get_mapped_doc(doctype, source_name,	{
+			# Invert the address on target doc creation
+			update_address(target_doc, 'company_address', 'company_address_display', source_doc.supplier_address)
+			update_address(target_doc, 'shipping_address_name', 'shipping_address', source_doc.shipping_address)
+			update_address(target_doc, 'customer_address', 'address_display', source_doc.shipping_address)
+
+			update_taxes(target_doc, party=target_doc.customer, party_type='Customer', company=target_doc.company,
+				doctype=target_doc.doctype, party_address=target_doc.customer_address,
+				company_address=target_doc.company_address, shipping_address_name=target_doc.shipping_address_name)
+
+	doclist = get_mapped_doc(doctype, source_name, {
 		doctype: {
 			"doctype": target_doctype,
 			"postprocess": update_details,
@@ -722,7 +738,10 @@
 		doctype +" Item": {
 			"doctype": target_doctype + " Item",
 			"field_map": {
-				source_document_warehouse_field: target_document_warehouse_field
+				source_document_warehouse_field: target_document_warehouse_field,
+				'name': 'delivery_note_item',
+				'batch_no': 'batch_no',
+				'serial_no': 'serial_no'
 			},
 			"field_no_map": [
 				"warehouse"
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 559f8be..d39b229 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -489,7 +489,10 @@
 	def test_closed_delivery_note(self):
 		from erpnext.stock.doctype.delivery_note.delivery_note import update_delivery_note_status
 
-		dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1", do_not_submit=True)
+		make_stock_entry(target="Stores - TCP1", qty=5, basic_rate=100)
+
+		dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1',
+			cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1", do_not_submit=True)
 
 		dn.submit()
 
diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
index 4bbf3de..1799624 100644
--- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
+++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
@@ -47,6 +47,7 @@
   "base_rate",
   "base_amount",
   "pricing_rules",
+  "stock_uom_rate",
   "is_free_item",
   "section_break_25",
   "net_rate",
@@ -458,7 +459,7 @@
    "fieldname": "warehouse",
    "fieldtype": "Link",
    "in_list_view": 1,
-   "label": "From Warehouse",
+   "label": "Warehouse",
    "oldfieldname": "warehouse",
    "oldfieldtype": "Link",
    "options": "Warehouse",
@@ -467,11 +468,12 @@
    "width": "100px"
   },
   {
+   "depends_on": "eval:parent.is_internal_customer",
    "fieldname": "target_warehouse",
    "fieldtype": "Link",
    "hidden": 1,
    "ignore_user_permissions": 1,
-   "label": "Customer Warehouse (Optional)",
+   "label": "Target Warehouse",
    "no_copy": 1,
    "options": "Warehouse",
    "print_hide": 1
@@ -742,13 +744,21 @@
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "depends_on": "eval: doc.uom != doc.stock_uom",
+   "fieldname": "stock_uom_rate",
+   "fieldtype": "Currency",
+   "label": "Rate of Stock UOM",
+   "options": "currency",
+   "read_only": 1
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-12-07 19:59:27.119856",
+ "modified": "2021-01-30 21:42:03.767968",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note Item",
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index faeeb57..ec32b0f 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -384,7 +384,10 @@
 					<a href="#stock-balance">' + __("Stock Levels") + '</a></h5>');
 				erpnext.item.item_dashboard = new erpnext.stock.ItemDashboard({
 					parent: section,
-					item_code: frm.doc.name
+					item_code: frm.doc.name,
+					page_length: 20,
+					method: 'erpnext.stock.dashboard.item_dashboard.get_data',
+					template: 'item_dashboard_list'
 				});
 				erpnext.item.item_dashboard.refresh();
 			});
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index d07b3dc..fcf7c26 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -106,9 +106,9 @@
   "item_tax_section_break",
   "taxes",
   "inspection_criteria",
+  "quality_inspection_template",
   "inspection_required_before_purchase",
   "inspection_required_before_delivery",
-  "quality_inspection_template",
   "manufacturing",
   "default_bom",
   "is_sub_contracted_item",
@@ -814,7 +814,6 @@
    "label": "Inspection Required before Delivery"
   },
   {
-   "depends_on": "eval:(doc.inspection_required_before_purchase || doc.inspection_required_before_delivery)",
    "fieldname": "quality_inspection_template",
    "fieldtype": "Link",
    "label": "Quality Inspection Template",
@@ -1069,7 +1068,7 @@
  "index_web_pages_for_search": 1,
  "links": [],
  "max_attachments": 1,
- "modified": "2020-08-07 14:24:58.384992",
+ "modified": "2021-01-25 20:49:50.222976",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Item",
@@ -1131,4 +1130,4 @@
  "sort_order": "DESC",
  "title_field": "item_name",
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index be845d9..cda1069 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -672,13 +672,14 @@
 		if not records: return
 		document = _("Stock Reconciliation") if len(records) == 1 else _("Stock Reconciliations")
 
-		msg = _("The items {0} and {1} are present in the following {2} : <br>"
-			.format(frappe.bold(old_name), frappe.bold(new_name), document))
+		msg = _("The items {0} and {1} are present in the following {2} : ").format(
+			frappe.bold(old_name), frappe.bold(new_name), document)
 
+		msg += '<br>'
 		msg += ', '.join([get_link_to_form("Stock Reconciliation", d.parent) for d in records]) + "<br><br>"
 
-		msg += _("Note: To merge the items, create a separate Stock Reconciliation for the old item {0}"
-			.format(frappe.bold(old_name)))
+		msg += _("Note: To merge the items, create a separate Stock Reconciliation for the old item {0}").format(
+			frappe.bold(old_name))
 
 		frappe.throw(_(msg), title=_("Merge not allowed"))
 
@@ -971,7 +972,7 @@
 							frappe.throw(_("As there are existing transactions against item {0}, you can not change the value of {1}").format(self.name, frappe.bold(self.meta.get_label(field))))
 
 	def check_if_linked_document_exists(self, field):
-		linked_doctypes = ["Delivery Note Item", "Sales Invoice Item", "Purchase Receipt Item",
+		linked_doctypes = ["Delivery Note Item", "Sales Invoice Item", "POS Invoice Item", "Purchase Receipt Item",
 			"Purchase Invoice Item", "Stock Entry Detail", "Stock Reconciliation Item"]
 
 		# For "Is Stock Item", following doctypes is important
diff --git a/erpnext/stock/doctype/item_price/item_price.js b/erpnext/stock/doctype/item_price/item_price.js
index 2729f4b..e4db048 100644
--- a/erpnext/stock/doctype/item_price/item_price.js
+++ b/erpnext/stock/doctype/item_price/item_price.js
@@ -15,5 +15,13 @@
 
 		frm.set_df_property("bulk_import_help", "options",
 			'<a href="#data-import-tool/Item Price">' + __("Import in Bulk") + '</a>');
+
+		frm.set_query('batch_no', function() {
+			return {
+				filters: {
+					'item': frm.doc.item_code
+				}
+			}
+		});
 	}
 });
diff --git a/erpnext/stock/doctype/item_price/item_price.json b/erpnext/stock/doctype/item_price/item_price.json
index 5f62381..83177b3 100644
--- a/erpnext/stock/doctype/item_price/item_price.json
+++ b/erpnext/stock/doctype/item_price/item_price.json
@@ -18,6 +18,7 @@
   "price_list",
   "customer",
   "supplier",
+  "batch_no",
   "column_break_3",
   "buying",
   "selling",
@@ -47,31 +48,41 @@
    "oldfieldtype": "Select",
    "options": "Item",
    "reqd": 1,
-   "search_index": 1
+   "search_index": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "uom",
    "fieldtype": "Link",
    "label": "UOM",
-   "options": "UOM"
+   "options": "UOM",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "default": "0",
    "description": "Quantity  that must be bought or sold per UOM",
    "fieldname": "packing_unit",
    "fieldtype": "Int",
-   "label": "Packing Unit"
+   "label": "Packing Unit",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "column_break_17",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "item_name",
    "fieldtype": "Data",
    "in_list_view": 1,
    "label": "Item Name",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fetch_from": "item_code.brand",
@@ -79,19 +90,25 @@
    "fieldtype": "Read Only",
    "in_list_view": 1,
    "label": "Brand",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "item_description",
    "fieldtype": "Text",
    "label": "Item Description",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "price_list_details",
    "fieldtype": "Section Break",
    "label": "Price List",
-   "options": "fa fa-tags"
+   "options": "fa fa-tags",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "price_list",
@@ -100,7 +117,9 @@
    "in_standard_filter": 1,
    "label": "Price List",
    "options": "Price List",
-   "reqd": 1
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "bold": 1,
@@ -108,37 +127,49 @@
    "fieldname": "customer",
    "fieldtype": "Link",
    "label": "Customer",
-   "options": "Customer"
+   "options": "Customer",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "depends_on": "eval:doc.buying == 1",
    "fieldname": "supplier",
    "fieldtype": "Link",
    "label": "Supplier",
-   "options": "Supplier"
+   "options": "Supplier",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "column_break_3",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "default": "0",
    "fieldname": "buying",
    "fieldtype": "Check",
    "label": "Buying",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "default": "0",
    "fieldname": "selling",
    "fieldtype": "Check",
    "label": "Selling",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "item_details",
    "fieldtype": "Section Break",
-   "options": "fa fa-tag"
+   "options": "fa fa-tag",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "bold": 1,
@@ -146,11 +177,15 @@
    "fieldtype": "Link",
    "label": "Currency",
    "options": "Currency",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "col_br_1",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "price_list_rate",
@@ -162,53 +197,80 @@
    "oldfieldname": "ref_rate",
    "oldfieldtype": "Currency",
    "options": "currency",
-   "reqd": 1
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "section_break_15",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "default": "Today",
    "fieldname": "valid_from",
    "fieldtype": "Date",
-   "label": "Valid From"
+   "label": "Valid From",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "default": "0",
    "fieldname": "lead_time_days",
    "fieldtype": "Int",
-   "label": "Lead Time in days"
+   "label": "Lead Time in days",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "column_break_18",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "valid_upto",
    "fieldtype": "Date",
-   "label": "Valid Upto"
+   "label": "Valid Upto",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "section_break_24",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "note",
    "fieldtype": "Text",
-   "label": "Note"
+   "label": "Note",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "reference",
    "fieldtype": "Data",
    "in_list_view": 1,
-   "label": "Reference"
+   "label": "Reference",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "batch_no",
+   "fieldtype": "Link",
+   "label": "Batch No",
+   "options": "Batch",
+   "show_days": 1,
+   "show_seconds": 1
   }
  ],
  "icon": "fa fa-flag",
  "idx": 1,
+ "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-07-06 22:31:32.943475",
+ "modified": "2020-12-08 18:12:15.395772",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Item Price",
diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py
index bed5ea9..e82a19b 100644
--- a/erpnext/stock/doctype/item_price/item_price.py
+++ b/erpnext/stock/doctype/item_price/item_price.py
@@ -54,7 +54,8 @@
 			"valid_upto",
 			"packing_unit",
 			"customer",
-			"supplier",]:
+			"supplier",
+			"batch_no"]:
 			if self.get(field):
 				conditions += " and {0} = %({0})s ".format(field)
 			else:
@@ -68,7 +69,7 @@
 			self.as_dict(),)
 
 		if price_list_rate:
-			frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty, and Dates."), ItemPriceDuplicateItem,)
+			frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, Batch, UOM, Qty, and Dates."), ItemPriceDuplicateItem,)
 
 	def before_save(self):
 		if self.selling:
diff --git a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
index 888bc2d..471e685 100644
--- a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
+++ b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
@@ -8,26 +8,32 @@
  "field_order": [
   "specification",
   "value",
+  "numeric",
   "column_break_3",
+  "min_value",
+  "max_value",
+  "formula_based_criteria",
   "acceptance_formula"
  ],
  "fields": [
   {
    "fieldname": "specification",
-   "fieldtype": "Data",
+   "fieldtype": "Link",
    "in_list_view": 1,
    "label": "Parameter",
    "oldfieldname": "specification",
    "oldfieldtype": "Data",
+   "options": "Quality Inspection Parameter",
    "print_width": "200px",
    "reqd": 1,
-   "width": "200px"
+   "width": "100px"
   },
   {
+   "depends_on": "eval:(!doc.formula_based_criteria && !doc.numeric)",
    "fieldname": "value",
    "fieldtype": "Data",
    "in_list_view": 1,
-   "label": "Acceptance Criteria",
+   "label": "Acceptance Criteria Value",
    "oldfieldname": "value",
    "oldfieldtype": "Data"
   },
@@ -36,17 +42,45 @@
    "fieldtype": "Column Break"
   },
   {
-   "description": "Simple Python formula based on numeric Readings.<br> Example 1: <b>reading_1 &gt; 0.2 and reading_1 &lt; 0.5</b><br>\nExample 2: <b>(reading_1 + reading_2) / 2 &lt; 10</b>",
+   "depends_on": "formula_based_criteria",
+   "description": "Simple Python formula applied on Reading fields.<br> Numeric eg. 1: <b>reading_1 &gt; 0.2 and reading_1 &lt; 0.5</b><br>\nNumeric eg. 2: <b>mean &gt; 3.5</b> (mean of populated fields)<br>\nValue based eg.:  <b>reading_value in (\"A\", \"B\", \"C\")</b>",
    "fieldname": "acceptance_formula",
    "fieldtype": "Code",
-   "in_list_view": 1,
    "label": "Acceptance Criteria Formula"
+  },
+  {
+   "default": "0",
+   "fieldname": "formula_based_criteria",
+   "fieldtype": "Check",
+   "label": "Formula Based Criteria"
+  },
+  {
+   "depends_on": "eval:(!doc.formula_based_criteria && doc.numeric)",
+   "fieldname": "min_value",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Minimum Value"
+  },
+  {
+   "depends_on": "eval:(!doc.formula_based_criteria && doc.numeric)",
+   "fieldname": "max_value",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Maximum Value"
+  },
+  {
+   "default": "1",
+   "fieldname": "numeric",
+   "fieldtype": "Check",
+   "in_list_view": 1,
+   "label": "Numeric",
+   "width": "80px"
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-11-16 16:33:42.421842",
+ "modified": "2021-02-01 19:18:46.924399",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Item Quality Inspection Parameter",
diff --git a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json
index b24d621..c77b993 100644
--- a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json
+++ b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "creation": "2013-02-22 01:28:02",
  "doctype": "DocType",
  "document_type": "Document",
@@ -29,6 +30,8 @@
    "options": "Item",
    "read_only": 1,
    "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1,
    "width": "100px"
   },
   {
@@ -41,6 +44,8 @@
    "print_width": "300px",
    "read_only": 1,
    "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1,
    "width": "120px"
   },
   {
@@ -50,7 +55,9 @@
    "no_copy": 1,
    "options": "Purchase Invoice\nPurchase Receipt",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "receipt_document",
@@ -59,25 +66,33 @@
    "no_copy": 1,
    "options": "receipt_document_type",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "col_break2",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "qty",
    "fieldtype": "Float",
    "in_list_view": 1,
    "label": "Qty",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "rate",
    "fieldtype": "Currency",
    "label": "Rate",
    "options": "Company:company:default_currency",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "amount",
@@ -88,14 +103,19 @@
    "oldfieldtype": "Currency",
    "options": "Company:company:default_currency",
    "read_only": 1,
-   "reqd": 1
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "applicable_charges",
    "fieldtype": "Currency",
    "in_list_view": 1,
    "label": "Applicable Charges",
-   "options": "Company:company:default_currency"
+   "options": "Company:company:default_currency",
+   "read_only_depends_on": "eval:parent.distribute_charges_based_on != 'Distribute Manually'",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "purchase_receipt_item",
@@ -104,22 +124,30 @@
    "label": "Purchase Receipt Item",
    "no_copy": 1,
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "cost_center",
    "fieldtype": "Link",
    "label": "Cost Center",
-   "options": "Cost Center"
+   "options": "Cost Center",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "accounting_dimensions_section",
    "fieldtype": "Section Break",
-   "label": "Accounting Dimensions"
+   "label": "Accounting Dimensions",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "dimension_col_break",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "default": "0",
@@ -128,12 +156,15 @@
    "fieldtype": "Check",
    "hidden": 1,
    "label": "Is Fixed Asset",
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   }
  ],
  "idx": 1,
  "istable": 1,
- "modified": "2020-09-18 17:26:09.703215",
+ "links": [],
+ "modified": "2021-01-25 23:09:23.322282",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Landed Cost Item",
diff --git a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
index 64331c7..4fcdb4c 100644
--- a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
+++ b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
@@ -6,8 +6,11 @@
  "engine": "InnoDB",
  "field_order": [
   "expense_account",
+  "account_currency",
+  "exchange_rate",
   "description",
   "col_break3",
+  "base_amount",
   "amount"
  ],
  "fields": [
@@ -28,7 +31,7 @@
    "fieldtype": "Currency",
    "in_list_view": 1,
    "label": "Amount",
-   "options": "Company:company:default_currency",
+   "options": "account_currency",
    "reqd": 1
   },
   {
@@ -38,13 +41,33 @@
    "in_list_view": 1,
    "label": "Expense Account",
    "mandatory_depends_on": "eval:cint(erpnext.is_perpetual_inventory_enabled(parent.company))",
-   "options": "Account",
-   "print_hide": 1
+   "options": "Account"
+  },
+  {
+   "fieldname": "account_currency",
+   "fieldtype": "Link",
+   "label": "Account Currency",
+   "options": "Currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "exchange_rate",
+   "fieldtype": "Float",
+   "label": "Exchange Rate",
+   "precision": "9"
+  },
+  {
+   "fieldname": "base_amount",
+   "fieldtype": "Currency",
+   "label": "Base Amount",
+   "options": "Company:company:default_currency",
+   "read_only": 1
   }
  ],
+ "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-12-04 00:22:14.373312",
+ "modified": "2020-12-26 01:07:23.233604",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Landed Cost Taxes and Charges",
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js
index 5de1352..1abbc35 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js
@@ -1,6 +1,7 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
+{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %};
 
 frappe.provide("erpnext.stock");
 
@@ -29,20 +30,9 @@
 		this.frm.add_fetch("receipt_document", "supplier", "supplier");
 		this.frm.add_fetch("receipt_document", "posting_date", "posting_date");
 		this.frm.add_fetch("receipt_document", "base_grand_total", "grand_total");
-
-		this.frm.set_query("expense_account", "taxes", function() {
-			return {
-				query: "erpnext.controllers.queries.tax_account_query",
-				filters: {
-					"account_type": ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"],
-					"company": me.frm.doc.company
-				}
-			};
-		});
-
 	},
 
-	refresh: function(frm) {
+	refresh: function() {
 		var help_content =
 			`<br><br>
 			<table class="table table-bordered" style="background-color: #f9f9f9;">
@@ -72,6 +62,11 @@
 			</table>`;
 
 		set_field_options("landed_cost_help", help_content);
+
+		if (this.frm.doc.company) {
+			let company_currency = frappe.get_doc(":Company", this.frm.doc.company).default_currency;
+			this.frm.set_currency_labels(["total_taxes_and_charges"], company_currency);
+		}
 	},
 
 	get_items_from_purchase_receipts: function() {
@@ -97,34 +92,36 @@
 	set_total_taxes_and_charges: function() {
 		var total_taxes_and_charges = 0.0;
 		$.each(this.frm.doc.taxes || [], function(i, d) {
-			total_taxes_and_charges += flt(d.amount)
+			total_taxes_and_charges += flt(d.base_amount);
 		});
-		cur_frm.set_value("total_taxes_and_charges", total_taxes_and_charges);
+		this.frm.set_value("total_taxes_and_charges", total_taxes_and_charges);
 	},
 
 	set_applicable_charges_for_item: function() {
 		var me = this;
 
 		if(this.frm.doc.taxes.length) {
-
 			var total_item_cost = 0.0;
 			var based_on = this.frm.doc.distribute_charges_based_on.toLowerCase();
-			$.each(this.frm.doc.items || [], function(i, d) {
-				total_item_cost += flt(d[based_on])
-			});
 
-			var total_charges = 0.0;
-			$.each(this.frm.doc.items || [], function(i, item) {
-				item.applicable_charges = flt(item[based_on]) * flt(me.frm.doc.total_taxes_and_charges) / flt(total_item_cost)
-				item.applicable_charges = flt(item.applicable_charges, precision("applicable_charges", item))
-				total_charges += item.applicable_charges
-			});
+			if (based_on != 'distribute manually') {
+				$.each(this.frm.doc.items || [], function(i, d) {
+					total_item_cost += flt(d[based_on])
+				});
 
-			if (total_charges != this.frm.doc.total_taxes_and_charges){
-				var diff = this.frm.doc.total_taxes_and_charges - flt(total_charges)
-				this.frm.doc.items.slice(-1)[0].applicable_charges += diff
+				var total_charges = 0.0;
+				$.each(this.frm.doc.items || [], function(i, item) {
+					item.applicable_charges = flt(item[based_on]) * flt(me.frm.doc.total_taxes_and_charges) / flt(total_item_cost)
+					item.applicable_charges = flt(item.applicable_charges, precision("applicable_charges", item))
+					total_charges += item.applicable_charges
+				});
+
+				if (total_charges != this.frm.doc.total_taxes_and_charges){
+					var diff = this.frm.doc.total_taxes_and_charges - flt(total_charges)
+					this.frm.doc.items.slice(-1)[0].applicable_charges += diff
+				}
+				refresh_field("items");
 			}
-			refresh_field("items");
 		}
 	},
 	distribute_charges_based_on: function (frm) {
@@ -134,7 +131,16 @@
 	items_remove: () => {
 		this.trigger('set_applicable_charges_for_item');
 	}
-
 });
 
 cur_frm.script_manager.make(erpnext.stock.LandedCostVoucher);
+
+frappe.ui.form.on('Landed Cost Taxes and Charges', {
+	expense_account: function(frm, cdt, cdn) {
+		frm.events.set_account_currency(frm, cdt, cdn);
+	},
+
+	amount: function(frm, cdt, cdn) {
+		frm.events.set_base_amount(frm, cdt, cdn);
+	}
+});
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json
index 0149280..059f925 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "autoname": "naming_series:",
  "creation": "2014-07-11 11:33:42.547339",
  "doctype": "DocType",
@@ -7,6 +8,9 @@
  "field_order": [
   "naming_series",
   "company",
+  "column_break_2",
+  "posting_date",
+  "section_break_5",
   "purchase_receipts",
   "purchase_receipt_items",
   "get_items_from_purchase_receipts",
@@ -30,7 +34,9 @@
    "options": "MAT-LCV-.YYYY.-",
    "print_hide": 1,
    "reqd": 1,
-   "set_only_once": 1
+   "set_only_once": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "company",
@@ -40,24 +46,32 @@
    "label": "Company",
    "options": "Company",
    "remember_last_selected_value": 1,
-   "reqd": 1
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "purchase_receipts",
    "fieldtype": "Table",
    "label": "Purchase Receipts",
    "options": "Landed Cost Purchase Receipt",
-   "reqd": 1
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "purchase_receipt_items",
    "fieldtype": "Section Break",
-   "label": "Purchase Receipt Items"
+   "label": "Purchase Receipt Items",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "get_items_from_purchase_receipts",
    "fieldtype": "Button",
-   "label": "Get Items From Purchase Receipts"
+   "label": "Get Items From Purchase Receipts",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "items",
@@ -65,42 +79,56 @@
    "label": "Purchase Receipt Items",
    "no_copy": 1,
    "options": "Landed Cost Item",
-   "reqd": 1
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "sec_break1",
    "fieldtype": "Section Break",
-   "label": "Applicable Charges"
+   "label": "Applicable Charges",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "taxes",
    "fieldtype": "Table",
    "label": "Taxes and Charges",
    "options": "Landed Cost Taxes and Charges",
-   "reqd": 1
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "section_break_9",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "total_taxes_and_charges",
    "fieldtype": "Currency",
-   "label": "Total Taxes and Charges",
+   "label": "Total Taxes and Charges (Company Currency)",
    "options": "Company:company:default_currency",
    "read_only": 1,
-   "reqd": 1
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "col_break1",
-   "fieldtype": "Column Break"
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "distribute_charges_based_on",
    "fieldtype": "Select",
    "label": "Distribute Charges Based On",
-   "options": "Qty\nAmount",
-   "reqd": 1
+   "options": "Qty\nAmount\nDistribute Manually",
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "amended_from",
@@ -109,21 +137,51 @@
    "no_copy": 1,
    "options": "Landed Cost Voucher",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "sec_break2",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "show_days": 1,
+   "show_seconds": 1
   },
   {
    "fieldname": "landed_cost_help",
    "fieldtype": "HTML",
-   "label": "Landed Cost Help"
+   "label": "Landed Cost Help",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "default": "Today",
+   "fieldname": "posting_date",
+   "fieldtype": "Date",
+   "label": "Posting Date",
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "section_break_5",
+   "fieldtype": "Section Break",
+   "hide_border": 1,
+   "show_days": 1,
+   "show_seconds": 1
   }
  ],
  "icon": "icon-usd",
+ "index_web_pages_for_search": 1,
  "is_submittable": 1,
- "modified": "2019-11-21 15:34:10.846093",
+ "links": [],
+ "modified": "2021-01-25 23:07:30.468423",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Landed Cost Voucher",
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
index 9ec6b89..69a8bf1 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
@@ -9,6 +9,7 @@
 from frappe.model.document import Document
 from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 from erpnext.accounts.doctype.account.account import get_account_currency
+from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals
 
 class LandedCostVoucher(Document):
 	def get_items_from_purchase_receipts(self):
@@ -39,13 +40,15 @@
 
 	def validate(self):
 		self.check_mandatory()
+		self.validate_purchase_receipts()
+		init_landed_taxes_and_totals(self)
+		self.set_total_taxes_and_charges()
 		if not self.get("items"):
 			self.get_items_from_purchase_receipts()
-		else:
-			self.validate_applicable_charges_for_item()
-		self.validate_purchase_receipts()
-		self.validate_expense_accounts()
-		self.set_total_taxes_and_charges()
+
+		self.set_applicable_charges_on_item()
+		self.validate_applicable_charges_for_item()
+
 
 	def check_mandatory(self):
 		if not self.get("purchase_receipts"):
@@ -73,21 +76,37 @@
 				frappe.throw(_("Row {0}: Cost center is required for an item {1}")
 					.format(item.idx, item.item_code))
 
-	def validate_expense_accounts(self):
-		company_currency = erpnext.get_company_currency(self.company)
-		for account in self.taxes:
-			if get_account_currency(account.expense_account) != company_currency:
-				frappe.throw(_("Row {}: Expense account currency should be same as company's default currency.").format(account.idx)
-					+ _("Please select expense account with account currency as {}.").format(frappe.bold(company_currency)),
-					title=_("Invalid Account Currency"))
-
 	def set_total_taxes_and_charges(self):
-		self.total_taxes_and_charges = sum([flt(d.amount) for d in self.get("taxes")])
+		self.total_taxes_and_charges = sum([flt(d.base_amount) for d in self.get("taxes")])
+
+	def set_applicable_charges_on_item(self):
+		if self.get('taxes') and self.distribute_charges_based_on != 'Distribute Manually':
+			total_item_cost = 0.0
+			total_charges = 0.0
+			item_count = 0
+			based_on_field = frappe.scrub(self.distribute_charges_based_on)
+
+			for item in self.get('items'):
+				total_item_cost += item.get(based_on_field)
+
+			for item in self.get('items'):
+				item.applicable_charges = flt(flt(item.get(based_on_field)) * (flt(self.total_taxes_and_charges) / flt(total_item_cost)),
+					item.precision('applicable_charges'))
+				total_charges += item.applicable_charges
+				item_count += 1
+
+			if total_charges != self.total_taxes_and_charges:
+				diff = self.total_taxes_and_charges - total_charges
+				self.get('items')[item_count - 1].applicable_charges += diff
 
 	def validate_applicable_charges_for_item(self):
 		based_on = self.distribute_charges_based_on.lower()
 
-		total = sum([flt(d.get(based_on)) for d in self.get("items")])
+		if based_on != 'distribute manually':
+			total = sum([flt(d.get(based_on)) for d in self.get("items")])
+		else:
+			# consider for proportion while distributing manually
+			total = sum([flt(d.get('applicable_charges')) for d in self.get("items")])
 
 		if not total:
 			frappe.throw(_("Total {0} for all items is zero, may be you should change 'Distribute Charges Based On'").format(based_on))
@@ -153,13 +172,13 @@
 				docs = frappe.db.get_all('Asset', filters={ receipt_document_type: item.receipt_document,
 					'item_code': item.item_code }, fields=['name', 'docstatus'])
 				if not docs or len(docs) != item.qty:
-					frappe.throw(_('There are not enough asset created or linked to {0}.').format(item.receipt_document)
-						+ _('Please create or link {0} Assets with respective document.').format(item.qty))
+					frappe.throw(_('There are not enough asset created or linked to {0}. Please create or link {1} Assets with respective document.').format(
+						item.receipt_document, item.qty))
 				if docs:
 					for d in docs:
 						if d.docstatus == 1:
-							frappe.throw(_('{0} {1} has submitted Assets. Remove Item {2} from table to continue.')
-								.format(item.receipt_document_type, frappe.bold(item.receipt_document), frappe.bold(item.item_code)))
+							frappe.throw(_('{2} <b>{0}</b> has submitted Assets. Remove Item <b>{1}</b> from table to continue.').format(
+									item.receipt_document, item.item_code, item.receipt_document_type))
 
 	def update_rate_in_serial_no_for_non_asset_items(self, receipt_document):
 		for item in receipt_document.get("items"):
diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
index b97213e..984ae46 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
@@ -10,6 +10,7 @@
 	import get_gl_entries, test_records as pr_test_records, make_purchase_receipt
 from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
 from erpnext.accounts.doctype.account.test_account import get_inventory_account
+from erpnext.accounts.doctype.account.test_account import create_account
 
 class TestLandedCostVoucher(unittest.TestCase):
 	def test_landed_cost_voucher(self):
@@ -147,7 +148,6 @@
 
 
 	def test_landed_cost_voucher_for_odd_numbers (self):
-
 		pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", do_not_save=True)
 		pr.items[0].cost_center = "Main - TCP1"
 		for x in range(2):
@@ -162,8 +162,8 @@
 
 		lcv = create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, 123.22)
 
-		self.assertEqual(lcv.items[0].applicable_charges, 41.07)
-		self.assertEqual(lcv.items[2].applicable_charges, 41.08)
+		self.assertEqual(flt(lcv.items[0].applicable_charges, 2), 41.07)
+		self.assertEqual(flt(lcv.items[2].applicable_charges, 2), 41.08)
 
 	def test_multiple_landed_cost_voucher_against_pr(self):
 		pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1",
@@ -206,6 +206,50 @@
 		self.assertEqual(pr.items[0].landed_cost_voucher_amount, 100)
 		self.assertEqual(pr.items[1].landed_cost_voucher_amount, 100)
 
+	def test_multi_currency_lcv(self):
+		from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records, save_new_records
+
+		save_new_records(test_records)
+
+		## Create USD Shipping charges_account
+		usd_shipping = create_account(account_name="Shipping Charges USD",
+			parent_account="Duties and Taxes - TCP1", company="_Test Company with perpetual inventory",
+			account_currency="USD")
+
+		pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1",
+			supplier_warehouse = "Stores - TCP1")
+		pr.submit()
+
+		lcv = make_landed_cost_voucher(company = pr.company, receipt_document_type = "Purchase Receipt",
+			receipt_document=pr.name, charges=100, do_not_save=True)
+
+		lcv.append("taxes", {
+			"description": "Shipping Charges",
+			"expense_account": usd_shipping,
+			"amount": 10
+		})
+
+		lcv.save()
+		lcv.submit()
+		pr.load_from_db()
+
+		# Considering exchange rate from USD to INR as 62.9
+		self.assertEqual(lcv.total_taxes_and_charges, 729)
+		self.assertEqual(pr.items[0].landed_cost_voucher_amount, 729)
+
+		gl_entries = frappe.get_all("GL Entry", fields=["account", "credit", "credit_in_account_currency"],
+			filters={"voucher_no": pr.name, "account": ("in", ["Shipping Charges USD - TCP1", "Expenses Included In Valuation - TCP1"])})
+
+		expected_gl_entries = {
+			"Shipping Charges USD - TCP1": [629, 10],
+			"Expenses Included In Valuation - TCP1": [100, 100]
+		}
+
+		for entry in gl_entries:
+			amounts = expected_gl_entries.get(entry.account)
+			self.assertEqual(entry.credit, amounts[0])
+			self.assertEqual(entry.credit_in_account_currency, amounts[1])
+
 def make_landed_cost_voucher(** args):
 	args = frappe._dict(args)
 	ref_doc = frappe.get_doc(args.receipt_document_type, args.receipt_document)
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index 01edd99..527b0d3 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -2,6 +2,7 @@
 // License: GNU General Public License v3. See license.txt
 
 // eslint-disable-next-line
+frappe.provide("erpnext.accounts.dimensions");
 {% include 'erpnext/public/js/controllers/buying.js' %};
 
 frappe.ui.form.on('Material Request', {
@@ -66,6 +67,12 @@
 				filters: {'company': doc.company}
 			};
 		});
+
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
+	},
+
+	company: function(frm) {
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
 	},
 
 	onload_post_render: function(frm) {
diff --git a/erpnext/stock/doctype/material_request/material_request_list.js b/erpnext/stock/doctype/material_request/material_request_list.js
index 0d70958..de7a3d0 100644
--- a/erpnext/stock/doctype/material_request/material_request_list.js
+++ b/erpnext/stock/doctype/material_request/material_request_list.js
@@ -1,9 +1,10 @@
 frappe.listview_settings['Material Request'] = {
 	add_fields: ["material_request_type", "status", "per_ordered", "per_received", "transfer_status"],
 	get_indicator: function(doc) {
-		if(doc.status=="Stopped") {
+		var precision = frappe.defaults.get_default("float_precision");
+		if (doc.status=="Stopped") {
 			return [__("Stopped"), "red", "status,=,Stopped"];
-		} else if(doc.transfer_status && doc.docstatus != 2) {
+		} else if (doc.transfer_status && doc.docstatus != 2) {
 			if (doc.transfer_status == "Not Started") {
 				return [__("Not Started"), "orange"];
 			} else if (doc.transfer_status == "In Transit") {
@@ -11,14 +12,14 @@
 			} else if (doc.transfer_status == "Completed") {
 				return [__("Completed"), "green"];
 			}
-		} else if(doc.docstatus==1 && flt(doc.per_ordered, 2) == 0) {
+		} else if (doc.docstatus==1 && flt(doc.per_ordered, precision) == 0) {
 			return [__("Pending"), "orange", "per_ordered,=,0"];
-		}  else if(doc.docstatus==1 && flt(doc.per_ordered, 2) < 100) {
+		}  else if (doc.docstatus==1 && flt(doc.per_ordered, precision) < 100) {
 			return [__("Partially ordered"), "yellow", "per_ordered,<,100"];
-		} else if(doc.docstatus==1 && flt(doc.per_ordered, 2) == 100) {
-			if (doc.material_request_type == "Purchase" && flt(doc.per_received, 2) < 100 && flt(doc.per_received, 2) > 0) {
+		} else if (doc.docstatus==1 && flt(doc.per_ordered, precision) == 100) {
+			if (doc.material_request_type == "Purchase" && flt(doc.per_received, precision) < 100 && flt(doc.per_received, precision) > 0) {
 				return [__("Partially Received"), "yellow", "per_received,<,100"];
-			} else if (doc.material_request_type == "Purchase" && flt(doc.per_received, 2) == 100) {
+			} else if (doc.material_request_type == "Purchase" && flt(doc.per_received, precision) == 100) {
 				return [__("Received"), "green", "per_received,=,100"];
 			} else if (doc.material_request_type == "Purchase") {
 				return [__("Ordered"), "green", "per_ordered,=,100"];
diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py
index 0a29fa0..72a3a5e 100644
--- a/erpnext/stock/doctype/material_request/test_material_request.py
+++ b/erpnext/stock/doctype/material_request/test_material_request.py
@@ -424,6 +424,7 @@
 			"basic_rate": 1.0
 		})
 		se_doc.get("items")[1].update({
+			"item_code": "_Test Item Home Desktop 100",
 			"qty": 3.0,
 			"transfer_qty": 3.0,
 			"s_warehouse": "_Test Warehouse 1 - _TC",
@@ -534,7 +535,7 @@
 
 		mr = make_material_request(item_code='_Test FG Item', material_request_type='Manufacture',
 			uom="_Test UOM 1", conversion_factor=12)
-		
+
 		requested_qty = self._get_requested_qty('_Test FG Item', '_Test Warehouse - _TC')
 
 		self.assertEqual(requested_qty, existing_requested_qty + 120)
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index bc1d81d..57cc350 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -46,6 +46,8 @@
 		erpnext.queries.setup_queries(frm, "Warehouse", function() {
 			return erpnext.queries.warehouse(frm.doc);
 		});
+
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
 	},
 
 	refresh: function(frm) {
@@ -75,6 +77,7 @@
 
 	company: function(frm) {
 		frm.trigger("toggle_display_account_head");
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
 	},
 
 	toggle_display_account_head: function(frm) {
@@ -213,6 +216,10 @@
 		});
 	},
 
+	apply_putaway_rule: function() {
+		if (this.frm.doc.apply_putaway_rule) erpnext.apply_putaway_rule(this.frm);
+	}
+
 });
 
 // for backward compatibility: combine new and previous states
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index 5bb3095..32d349f 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -21,6 +21,7 @@
   "posting_date",
   "posting_time",
   "set_posting_time",
+  "apply_putaway_rule",
   "is_return",
   "return_against",
   "section_addresses",
@@ -47,6 +48,7 @@
   "set_warehouse",
   "rejected_warehouse",
   "col_break_warehouse",
+  "set_from_warehouse",
   "is_subcontracted",
   "supplier_warehouse",
   "items_section",
@@ -114,6 +116,7 @@
   "per_returned",
   "is_internal_supplier",
   "inter_company_reference",
+  "represents_company",
   "subscription_detail",
   "auto_repeat",
   "printing_settings",
@@ -1086,7 +1089,9 @@
    "fieldname": "inter_company_reference",
    "fieldtype": "Link",
    "label": "Inter Company Reference",
+   "no_copy": 1,
    "options": "Delivery Note",
+   "print_hide": 1,
    "read_only": 1
   },
   {
@@ -1107,6 +1112,12 @@
    "read_only": 1
   },
   {
+   "default": "0",
+   "fieldname": "apply_putaway_rule",
+   "fieldtype": "Check",
+   "label": "Apply Putaway Rule"
+  },
+  {
    "depends_on": "eval:!doc.__islocal",
    "fieldname": "per_returned",
    "fieldtype": "Percent",
@@ -1114,13 +1125,29 @@
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "depends_on": "eval: doc.is_internal_supplier",
+   "description": "Sets 'From Warehouse' in each row of the items table.",
+   "fieldname": "set_from_warehouse",
+   "fieldtype": "Link",
+   "label": "Set From Warehouse",
+   "options": "Warehouse"
+  },
+  {
+   "fetch_from": "supplier.represents_company",
+   "fieldname": "represents_company",
+   "fieldtype": "Link",
+   "label": "Represents Company",
+   "options": "Company",
+   "read_only": 1
   }
  ],
  "icon": "fa fa-truck",
  "idx": 261,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-11-30 12:54:23.278500",
+ "modified": "2020-12-26 20:49:39.106049",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 159a608..70687bda 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -83,6 +83,12 @@
 				}
 			])
 
+	def before_validate(self):
+		from erpnext.stock.doctype.putaway_rule.putaway_rule import apply_putaway_rule
+
+		if self.get("items") and self.apply_putaway_rule and not self.get("is_return"):
+			apply_putaway_rule(self.doctype, self.get("items"), self.company)
+
 	def validate(self):
 		self.validate_posting_time()
 		super(PurchaseReceipt, self).validate()
@@ -103,6 +109,7 @@
 		if getdate(self.posting_date) > getdate(nowdate()):
 			throw(_("Posting Date cannot be future date"))
 
+
 	def validate_cwip_accounts(self):
 		for item in self.get('items'):
 			if item.is_fixed_asset and is_cwip_accounting_enabled(item.asset_category):
@@ -281,12 +288,16 @@
 					# Amount added through landed-cost-voucher
 					if d.landed_cost_voucher_amount and landed_cost_entries:
 						for account, amount in iteritems(landed_cost_entries[(d.item_code, d.name)]):
+							account_currency = get_account_currency(account)
 							gl_entries.append(self.get_gl_dict({
 								"account": account,
+								"account_currency": account_currency,
 								"against": warehouse_account[d.warehouse]["account"],
 								"cost_center": d.cost_center,
 								"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-								"credit": flt(amount),
+								"credit": (flt(amount["base_amount"]) if (amount["base_amount"] or
+									account_currency!=self.company_currency) else flt(amount["amount"])),
+								"credit_in_account_currency": flt(amount["amount"]),
 								"project": d.project
 							}, item=d))
 
@@ -313,10 +324,12 @@
 						else:
 							loss_account = self.get_company_default("default_expense_account")
 
+						cost_center = d.cost_center or frappe.get_cached_value("Company", self.company, "cost_center")
+
 						gl_entries.append(self.get_gl_dict({
 							"account": loss_account,
 							"against": warehouse_account[d.warehouse]["account"],
-							"cost_center": d.cost_center,
+							"cost_center": cost_center,
 							"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
 							"debit": divisional_loss,
 							"project": d.project
@@ -325,7 +338,7 @@
 				elif d.warehouse not in warehouse_with_no_account or \
 					d.rejected_warehouse not in warehouse_with_no_account:
 						warehouse_with_no_account.append(d.warehouse)
-			elif d.item_code not in stock_items and flt(d.qty) and auto_accounting_for_non_stock_items:
+			elif d.item_code not in stock_items and not d.is_fixed_asset and flt(d.qty) and auto_accounting_for_non_stock_items:
 
 				service_received_but_not_billed_account = self.get_company_default("service_received_but_not_billed")
 				credit_currency = get_account_currency(service_received_but_not_billed_account)
@@ -408,7 +421,7 @@
 		if warehouse_with_no_account:
 			frappe.msgprint(_("No accounting entries for the following warehouses") + ": \n" +
 				"\n".join(warehouse_with_no_account))
-		
+
 		return process_gl_map(gl_entries)
 
 	def get_asset_gl_entry(self, gl_entries):
@@ -721,7 +734,13 @@
 
 	for lcv in landed_cost_vouchers:
 		landed_cost_voucher_doc = frappe.get_doc("Landed Cost Voucher", lcv.parent)
-		based_on_field = frappe.scrub(landed_cost_voucher_doc.distribute_charges_based_on)
+
+		#Use amount field for total item cost for manually cost distributed LCVs
+		if landed_cost_voucher_doc.distribute_charges_based_on == 'Distribute Manually':
+			based_on_field = 'amount'
+		else:
+			based_on_field = frappe.scrub(landed_cost_voucher_doc.distribute_charges_based_on)
+
 		total_item_cost = 0
 
 		for item in landed_cost_voucher_doc.items:
@@ -731,9 +750,16 @@
 			if item.receipt_document == purchase_document:
 				for account in landed_cost_voucher_doc.taxes:
 					item_account_wise_cost.setdefault((item.item_code, item.purchase_receipt_item), {})
-					item_account_wise_cost[(item.item_code, item.purchase_receipt_item)].setdefault(account.expense_account, 0.0)
-					item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account] += \
+					item_account_wise_cost[(item.item_code, item.purchase_receipt_item)].setdefault(account.expense_account, {
+						"amount": 0.0,
+						"base_amount": 0.0
+					})
+
+					item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account]["amount"] += \
 						account.amount * item.get(based_on_field) / total_item_cost
 
+					item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account]["base_amount"] += \
+						account.base_amount * item.get(based_on_field) / total_item_cost
+
 	return item_account_wise_cost
 
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 83012d3..7741ee7 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -94,10 +94,15 @@
 		frappe.get_doc('Payment Terms Template', '_Test Payment Terms Template For Purchase Invoice').delete()
 
 	def test_purchase_receipt_no_gl_entry(self):
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+
 		company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company')
 
-		existing_bin_stock_value = frappe.db.get_value("Bin", {"item_code": "_Test Item",
-			"warehouse": "_Test Warehouse - _TC"}, "stock_value")
+		existing_bin_qty, existing_bin_stock_value = frappe.db.get_value("Bin", {"item_code": "_Test Item",
+			"warehouse": "_Test Warehouse - _TC"}, ["actual_qty", "stock_value"])
+
+		if existing_bin_qty < 0:
+			make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=abs(existing_bin_qty))
 
 		pr = make_purchase_receipt()
 
@@ -575,7 +580,7 @@
 
 		se = make_stock_entry(item_code=item_code, target="_Test Warehouse - _TC", qty=1,
 			serial_no=serial_no, basic_rate=100, do_not_submit=True)
-		self.assertRaises(SerialNoDuplicateError, se.submit)
+		se.submit()
 
 		dn.cancel()
 		pr1.cancel()
@@ -1011,6 +1016,7 @@
 	pr.currency = args.currency or "INR"
 	pr.is_return = args.is_return
 	pr.return_against = args.return_against
+	pr.apply_putaway_rule = args.apply_putaway_rule
 	qty = args.qty or 5
 	received_qty = args.received_qty or qty
 	rejected_qty = args.rejected_qty or flt(received_qty) - flt(qty)
@@ -1026,6 +1032,7 @@
 		"rejected_warehouse": args.rejected_warehouse or "_Test Rejected Warehouse - _TC" if rejected_qty != 0 else "",
 		"rate": args.rate if args.rate != None else 50,
 		"conversion_factor": args.conversion_factor or 1.0,
+		"stock_qty": flt(qty) * (flt(args.conversion_factor) or 1.0),
 		"serial_no": args.serial_no,
 		"stock_uom": args.stock_uom or "_Test UOM",
 		"uom": uom,
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
index 871b255..8974ad9 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -48,6 +48,7 @@
   "base_rate",
   "base_amount",
   "pricing_rules",
+  "stock_uom_rate",
   "is_free_item",
   "section_break_29",
   "net_rate",
@@ -76,6 +77,8 @@
   "purchase_order_item",
   "material_request_item",
   "purchase_receipt_item",
+  "delivery_note_item",
+  "putaway_rule",
   "section_break_45",
   "allow_zero_valuation_rate",
   "bom",
@@ -818,11 +821,12 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:parent.is_internal_supplier",
    "fieldname": "from_warehouse",
    "fieldtype": "Link",
    "hidden": 1,
    "ignore_user_permissions": 1,
-   "label": "Supplier Warehouse",
+   "label": "From Warehouse",
    "options": "Warehouse"
   },
   {
@@ -840,6 +844,15 @@
    "fieldtype": "Column Break"
   },
   {
+   "fieldname": "putaway_rule",
+   "fieldtype": "Link",
+   "label": "Putaway Rule",
+   "no_copy": 1,
+   "options": "Putaway Rule",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
    "fieldname": "tracking_section",
    "fieldtype": "Section Break"
   },
@@ -861,12 +874,28 @@
    "fieldtype": "Float",
    "label": "Received Qty in Stock UOM",
    "print_hide": 1
+  },
+  {
+   "depends_on": "eval: doc.uom != doc.stock_uom",
+   "fieldname": "stock_uom_rate",
+   "fieldtype": "Currency",
+   "label": "Rate of Stock UOM",
+   "options": "currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "delivery_note_item",
+   "fieldtype": "Data",
+   "label": "Delivery Note Item",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-12-07 10:00:38.204294",
+ "modified": "2021-01-30 21:44:06.918515",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt Item",
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/stock/doctype/putaway_rule/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/stock/doctype/putaway_rule/__init__.py
diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.js b/erpnext/stock/doctype/putaway_rule/putaway_rule.js
new file mode 100644
index 0000000..e056920
--- /dev/null
+++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.js
@@ -0,0 +1,43 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Putaway Rule', {
+	setup: function(frm) {
+		frm.set_query("warehouse", function() {
+			return {
+				"filters": {
+					"company": frm.doc.company,
+					"is_group": 0
+				}
+			};
+		});
+	},
+
+	uom: function(frm) {
+		if (frm.doc.item_code && frm.doc.uom) {
+			return frm.call({
+				method: "erpnext.stock.get_item_details.get_conversion_factor",
+				args: {
+					item_code: frm.doc.item_code,
+					uom: frm.doc.uom
+				},
+				callback: function(r) {
+					if (!r.exc) {
+						let stock_capacity = flt(frm.doc.capacity) * flt(r.message.conversion_factor);
+						frm.set_value('conversion_factor', r.message.conversion_factor);
+						frm.set_value('stock_capacity', stock_capacity);
+					}
+				}
+			});
+		}
+	},
+
+	capacity: function(frm) {
+		let stock_capacity = flt(frm.doc.capacity) * flt(frm.doc.conversion_factor);
+		frm.set_value('stock_capacity', stock_capacity);
+	}
+
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.json b/erpnext/stock/doctype/putaway_rule/putaway_rule.json
new file mode 100644
index 0000000..a003f49
--- /dev/null
+++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.json
@@ -0,0 +1,160 @@
+{
+ "actions": [],
+ "autoname": "PUT-.####",
+ "creation": "2020-11-09 11:39:46.489501",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "disable",
+  "item_code",
+  "item_name",
+  "warehouse",
+  "priority",
+  "col_break_capacity",
+  "company",
+  "capacity",
+  "uom",
+  "conversion_factor",
+  "stock_uom",
+  "stock_capacity"
+ ],
+ "fields": [
+  {
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "in_standard_filter": 1,
+   "label": "Item",
+   "options": "Item",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "item_code.item_name",
+   "fieldname": "item_name",
+   "fieldtype": "Data",
+   "label": "Item Name",
+   "read_only": 1
+  },
+  {
+   "fieldname": "warehouse",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Warehouse",
+   "options": "Warehouse",
+   "reqd": 1
+  },
+  {
+   "fieldname": "col_break_capacity",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "capacity",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Capacity",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "item_code.stock_uom",
+   "fieldname": "stock_uom",
+   "fieldtype": "Link",
+   "label": "Stock UOM",
+   "options": "UOM",
+   "read_only": 1
+  },
+  {
+   "default": "1",
+   "fieldname": "priority",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Priority"
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "in_standard_filter": 1,
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:!doc.__islocal",
+   "fieldname": "disable",
+   "fieldtype": "Check",
+   "label": "Disable"
+  },
+  {
+   "fieldname": "uom",
+   "fieldtype": "Link",
+   "label": "UOM",
+   "no_copy": 1,
+   "options": "UOM"
+  },
+  {
+   "fieldname": "stock_capacity",
+   "fieldtype": "Float",
+   "label": "Capacity in Stock UOM",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "default": "1",
+   "fieldname": "conversion_factor",
+   "fieldtype": "Float",
+   "label": "Conversion Factor",
+   "no_copy": 1,
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2020-11-25 20:39:19.973437",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Putaway Rule",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Stock Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Stock User",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "permlevel": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Stock Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "item_code",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.py b/erpnext/stock/doctype/putaway_rule/putaway_rule.py
new file mode 100644
index 0000000..ea26cac
--- /dev/null
+++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.py
@@ -0,0 +1,235 @@
+# -*- 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
+import copy
+import json
+from collections import defaultdict
+from six import string_types
+from frappe import _
+from frappe.utils import flt, floor, nowdate, cint
+from frappe.model.document import Document
+from erpnext.stock.utils import get_stock_balance
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
+class PutawayRule(Document):
+	def validate(self):
+		self.validate_duplicate_rule()
+		self.validate_warehouse_and_company()
+		self.validate_capacity()
+		self.validate_priority()
+		self.set_stock_capacity()
+
+	def validate_duplicate_rule(self):
+		existing_rule = frappe.db.exists("Putaway Rule", {"item_code": self.item_code, "warehouse": self.warehouse})
+		if existing_rule and existing_rule != self.name:
+			frappe.throw(_("Putaway Rule already exists for Item {0} in Warehouse {1}.")
+				.format(frappe.bold(self.item_code), frappe.bold(self.warehouse)),
+				title=_("Duplicate"))
+
+	def validate_priority(self):
+		if self.priority < 1:
+			frappe.throw(_("Priority cannot be lesser than 1."), title=_("Invalid Priority"))
+
+	def validate_warehouse_and_company(self):
+		company = frappe.db.get_value("Warehouse", self.warehouse, "company")
+		if company != self.company:
+			frappe.throw(_("Warehouse {0} does not belong to Company {1}.")
+				.format(frappe.bold(self.warehouse), frappe.bold(self.company)),
+				title=_("Invalid Warehouse"))
+
+	def validate_capacity(self):
+		stock_uom = frappe.db.get_value("Item", self.item_code, "stock_uom")
+		balance_qty = get_stock_balance(self.item_code, self.warehouse, nowdate())
+
+		if flt(self.stock_capacity) < flt(balance_qty):
+			frappe.throw(_("Warehouse Capacity for Item '{0}' must be greater than the existing stock level of {1} {2}.")
+				.format(self.item_code, frappe.bold(balance_qty), stock_uom),
+				title=_("Insufficient Capacity"))
+
+		if not self.capacity:
+			frappe.throw(_("Capacity must be greater than 0"), title=_("Invalid"))
+
+	def set_stock_capacity(self):
+		self.stock_capacity = (flt(self.conversion_factor) or 1) * flt(self.capacity)
+
+@frappe.whitelist()
+def get_available_putaway_capacity(rule):
+	stock_capacity, item_code, warehouse = frappe.db.get_value("Putaway Rule", rule,
+		["stock_capacity", "item_code", "warehouse"])
+	balance_qty = get_stock_balance(item_code, warehouse, nowdate())
+	free_space = flt(stock_capacity) - flt(balance_qty)
+	return free_space if free_space > 0 else 0
+
+@frappe.whitelist()
+def apply_putaway_rule(doctype, items, company, sync=None, purpose=None):
+	""" Applies Putaway Rule on line items.
+
+		items: List of Purchase Receipt/Stock Entry Items
+		company: Company in the Purchase Receipt/Stock Entry
+		doctype: Doctype to apply rule on
+		purpose: Purpose of Stock Entry
+		sync (optional): Sync with client side only for client side calls
+	"""
+	if isinstance(items, string_types):
+		items = json.loads(items)
+
+	items_not_accomodated, updated_table = [], []
+	item_wise_rules = defaultdict(list)
+
+	for item in items:
+		if isinstance(item, dict):
+			item = frappe._dict(item)
+
+		source_warehouse = item.get("s_warehouse")
+		serial_nos = get_serial_nos(item.get("serial_no"))
+		item.conversion_factor = flt(item.conversion_factor) or 1.0
+		pending_qty, item_code = flt(item.qty), item.item_code
+		pending_stock_qty = flt(item.transfer_qty) if doctype == "Stock Entry" else flt(item.stock_qty)
+		uom_must_be_whole_number = frappe.db.get_value('UOM', item.uom, 'must_be_whole_number')
+
+		if not pending_qty or not item_code:
+			updated_table = add_row(item, pending_qty, source_warehouse or item.warehouse, updated_table)
+			continue
+
+		at_capacity, rules = get_ordered_putaway_rules(item_code, company, source_warehouse=source_warehouse)
+
+		if not rules:
+			warehouse = source_warehouse or item.warehouse
+			if at_capacity:
+				# rules available, but no free space
+				items_not_accomodated.append([item_code, pending_qty])
+			else:
+				updated_table = add_row(item, pending_qty, warehouse, updated_table)
+			continue
+
+		# maintain item/item-warehouse wise rules, to handle if item is entered twice
+		# in the table, due to different price, etc.
+		key = item_code
+		if doctype == "Stock Entry" and purpose == "Material Transfer" and source_warehouse:
+			key = (item_code, source_warehouse)
+
+		if not item_wise_rules[key]:
+			item_wise_rules[key] = rules
+
+		for rule in item_wise_rules[key]:
+			if pending_stock_qty > 0 and rule.free_space:
+				stock_qty_to_allocate = flt(rule.free_space) if pending_stock_qty >= flt(rule.free_space) else pending_stock_qty
+				qty_to_allocate = stock_qty_to_allocate / item.conversion_factor
+
+				if uom_must_be_whole_number:
+					qty_to_allocate = floor(qty_to_allocate)
+					stock_qty_to_allocate = qty_to_allocate * item.conversion_factor
+
+				if not qty_to_allocate: break
+
+				updated_table = add_row(item, qty_to_allocate, rule.warehouse, updated_table,
+					rule.name, serial_nos=serial_nos)
+
+				pending_stock_qty -= stock_qty_to_allocate
+				pending_qty -= qty_to_allocate
+				rule["free_space"] -= stock_qty_to_allocate
+
+				if not pending_stock_qty > 0: break
+
+		# if pending qty after applying all rules, add row without warehouse
+		if pending_stock_qty > 0:
+			items_not_accomodated.append([item.item_code, pending_qty])
+
+	if items_not_accomodated:
+		show_unassigned_items_message(items_not_accomodated)
+
+	items[:] = updated_table if updated_table else items # modify items table
+
+	if sync and json.loads(sync): # sync with client side
+		return items
+
+def get_ordered_putaway_rules(item_code, company, source_warehouse=None):
+	"""Returns an ordered list of putaway rules to apply on an item."""
+	filters = {
+		"item_code": item_code,
+		"company": company,
+		"disable": 0
+	}
+	if source_warehouse:
+		filters.update({"warehouse": ["!=", source_warehouse]})
+
+	rules = frappe.get_all("Putaway Rule",
+		fields=["name", "item_code", "stock_capacity", "priority", "warehouse"],
+		filters=filters,
+		order_by="priority asc, capacity desc")
+
+	if not rules:
+		return False, None
+
+	vacant_rules = []
+	for rule in rules:
+		balance_qty = get_stock_balance(rule.item_code, rule.warehouse, nowdate())
+		free_space = flt(rule.stock_capacity) - flt(balance_qty)
+		if free_space > 0:
+			rule["free_space"] = free_space
+			vacant_rules.append(rule)
+
+	if not vacant_rules:
+		# After iterating through rules, if no rules are left
+		# then there is not enough space left in any rule
+		return True, None
+
+	vacant_rules = sorted(vacant_rules, key = lambda i: (i['priority'], -i['free_space']))
+
+	return False, vacant_rules
+
+def add_row(item, to_allocate, warehouse, updated_table, rule=None, serial_nos=None):
+	new_updated_table_row = copy.deepcopy(item)
+	new_updated_table_row.idx = 1 if not updated_table else cint(updated_table[-1].idx) + 1
+	new_updated_table_row.name = None
+	new_updated_table_row.qty = to_allocate
+
+	if item.doctype == "Stock Entry Detail":
+		new_updated_table_row.t_warehouse = warehouse
+		new_updated_table_row.transfer_qty = flt(to_allocate) * flt(new_updated_table_row.conversion_factor)
+	else:
+		new_updated_table_row.stock_qty = flt(to_allocate) * flt(new_updated_table_row.conversion_factor)
+		new_updated_table_row.warehouse = warehouse
+		new_updated_table_row.rejected_qty = 0
+		new_updated_table_row.received_qty = to_allocate
+
+	if rule:
+		new_updated_table_row.putaway_rule = rule
+	if serial_nos:
+		new_updated_table_row.serial_no = get_serial_nos_to_allocate(serial_nos, to_allocate)
+
+	updated_table.append(new_updated_table_row)
+	return updated_table
+
+def show_unassigned_items_message(items_not_accomodated):
+	msg = _("The following Items, having Putaway Rules, could not be accomodated:") + "<br><br>"
+	formatted_item_rows = ""
+
+	for entry in items_not_accomodated:
+		item_link = frappe.utils.get_link_to_form("Item", entry[0])
+		formatted_item_rows += """
+			<td>{0}</td>
+			<td>{1}</td>
+		</tr>""".format(item_link, frappe.bold(entry[1]))
+
+	msg += """
+		<table class="table">
+			<thead>
+				<td>{0}</td>
+				<td>{1}</td>
+			</thead>
+			{2}
+		</table>
+	""".format(_("Item"), _("Unassigned Qty"), formatted_item_rows)
+
+	frappe.msgprint(msg, title=_("Insufficient Capacity"), is_minimizable=True, wide=True)
+
+def get_serial_nos_to_allocate(serial_nos, to_allocate):
+	if serial_nos:
+		allocated_serial_nos = serial_nos[0: cint(to_allocate)]
+		serial_nos[:] = serial_nos[cint(to_allocate):] # pop out allocated serial nos and modify list
+		return "\n".join(allocated_serial_nos) if allocated_serial_nos else ""
+	else: return ""
\ No newline at end of file
diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule_list.js b/erpnext/stock/doctype/putaway_rule/putaway_rule_list.js
new file mode 100644
index 0000000..725e91e
--- /dev/null
+++ b/erpnext/stock/doctype/putaway_rule/putaway_rule_list.js
@@ -0,0 +1,18 @@
+frappe.listview_settings['Putaway Rule'] = {
+	add_fields: ["disable"],
+	get_indicator: (doc) => {
+		if (doc.disable) {
+			return [__("Disabled"), "darkgrey", "disable,=,1"];
+		} else {
+			return [__("Active"), "blue", "disable,=,0"];
+		}
+	},
+
+	reports: [
+		{
+			name: 'Warehouse Capacity Summary',
+			report_type: 'Page',
+			route: 'warehouse-capacity-summary'
+		}
+	]
+};
diff --git a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
new file mode 100644
index 0000000..86f7dc3
--- /dev/null
+++ b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
@@ -0,0 +1,389 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+import frappe
+import unittest
+from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.stock.get_item_details import get_conversion_factor
+from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+from erpnext.stock.doctype.batch.test_batch import make_new_batch
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+
+class TestPutawayRule(unittest.TestCase):
+	def setUp(self):
+		if not frappe.db.exists("Item", "_Rice"):
+			make_item("_Rice", {
+				'is_stock_item': 1,
+				'has_batch_no' : 1,
+				'create_new_batch': 1,
+				'stock_uom': 'Kg'
+			})
+
+		if not frappe.db.exists("Warehouse", {"warehouse_name": "Rack 1"}):
+			create_warehouse("Rack 1")
+		if not frappe.db.exists("Warehouse", {"warehouse_name": "Rack 2"}):
+			create_warehouse("Rack 2")
+
+		self.warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"})
+		self.warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"})
+
+		if not frappe.db.exists("UOM", "Bag"):
+			new_uom = frappe.new_doc("UOM")
+			new_uom.uom_name = "Bag"
+			new_uom.save()
+
+	def test_putaway_rules_priority(self):
+		"""Test if rule is applied by priority, irrespective of free space."""
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
+			uom="Kg")
+		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=300,
+			uom="Kg", priority=2)
+
+		pr = make_purchase_receipt(item_code="_Rice", qty=300, apply_putaway_rule=1,
+			do_not_submit=1)
+		self.assertEqual(len(pr.items), 2)
+		self.assertEqual(pr.items[0].qty, 200)
+		self.assertEqual(pr.items[0].warehouse, self.warehouse_1)
+		self.assertEqual(pr.items[1].qty, 100)
+		self.assertEqual(pr.items[1].warehouse, self.warehouse_2)
+
+		pr.delete()
+		rule_1.delete()
+		rule_2.delete()
+
+	def test_putaway_rules_with_same_priority(self):
+		"""Test if rule with more free space is applied,
+		among two rules with same priority and capacity."""
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=500,
+			uom="Kg")
+		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=500,
+			uom="Kg")
+
+		# out of 500 kg capacity, occupy 100 kg in warehouse_1
+		stock_receipt = make_stock_entry(item_code="_Rice", target=self.warehouse_1, qty=100, basic_rate=50)
+
+		pr = make_purchase_receipt(item_code="_Rice", qty=700, apply_putaway_rule=1,
+			do_not_submit=1)
+		self.assertEqual(len(pr.items), 2)
+		self.assertEqual(pr.items[0].qty, 500)
+		# warehouse_2 has 500 kg free space, it is given priority
+		self.assertEqual(pr.items[0].warehouse, self.warehouse_2)
+		self.assertEqual(pr.items[1].qty, 200)
+		# warehouse_1 has 400 kg free space, it is given less priority
+		self.assertEqual(pr.items[1].warehouse, self.warehouse_1)
+
+		stock_receipt.cancel()
+		pr.delete()
+		rule_1.delete()
+		rule_2.delete()
+
+	def test_putaway_rules_with_insufficient_capacity(self):
+		"""Test if qty exceeding capacity, is handled."""
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=100,
+			uom="Kg")
+		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=200,
+			uom="Kg")
+
+		pr = make_purchase_receipt(item_code="_Rice", qty=350, apply_putaway_rule=1,
+			do_not_submit=1)
+		self.assertEqual(len(pr.items), 2)
+		self.assertEqual(pr.items[0].qty, 200)
+		self.assertEqual(pr.items[0].warehouse, self.warehouse_2)
+		self.assertEqual(pr.items[1].qty, 100)
+		self.assertEqual(pr.items[1].warehouse, self.warehouse_1)
+		# total 300 assigned, 50 unassigned
+
+		pr.delete()
+		rule_1.delete()
+		rule_2.delete()
+
+	def test_putaway_rules_multi_uom(self):
+		"""Test rules applied on uom other than stock uom."""
+		item = frappe.get_doc("Item", "_Rice")
+		if not frappe.db.get_value("UOM Conversion Detail", {"parent": "_Rice", "uom": "Bag"}):
+			item.append("uoms", {
+				"uom": "Bag",
+				"conversion_factor": 1000
+			})
+			item.save()
+
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=3,
+			uom="Bag")
+		self.assertEqual(rule_1.stock_capacity, 3000)
+		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=4,
+			uom="Bag")
+		self.assertEqual(rule_2.stock_capacity, 4000)
+
+		# populate 'Rack 1' with 1 Bag, making the free space 2 Bags
+		stock_receipt = make_stock_entry(item_code="_Rice", target=self.warehouse_1, qty=1000, basic_rate=50)
+
+		pr = make_purchase_receipt(item_code="_Rice", qty=6, uom="Bag", stock_uom="Kg",
+			conversion_factor=1000, apply_putaway_rule=1, do_not_submit=1)
+		self.assertEqual(len(pr.items), 2)
+		self.assertEqual(pr.items[0].qty, 4)
+		self.assertEqual(pr.items[0].warehouse, self.warehouse_2)
+		self.assertEqual(pr.items[1].qty, 2)
+		self.assertEqual(pr.items[1].warehouse, self.warehouse_1)
+
+		stock_receipt.cancel()
+		pr.delete()
+		rule_1.delete()
+		rule_2.delete()
+
+	def test_putaway_rules_multi_uom_whole_uom(self):
+		"""Test if whole UOMs are handled."""
+		item = frappe.get_doc("Item", "_Rice")
+		if not frappe.db.get_value("UOM Conversion Detail", {"parent": "_Rice", "uom": "Bag"}):
+			item.append("uoms", {
+				"uom": "Bag",
+				"conversion_factor": 1000
+			})
+			item.save()
+
+		frappe.db.set_value("UOM", "Bag", "must_be_whole_number", 1)
+
+		# Putaway Rule in different UOM
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=1,
+			uom="Bag")
+		self.assertEqual(rule_1.stock_capacity, 1000)
+		# Putaway Rule in Stock UOM
+		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=500)
+		self.assertEqual(rule_2.stock_capacity, 500)
+		# total capacity is 1500 Kg
+
+		pr = make_purchase_receipt(item_code="_Rice", qty=2, uom="Bag", stock_uom="Kg",
+			conversion_factor=1000, apply_putaway_rule=1, do_not_submit=1)
+		self.assertEqual(len(pr.items), 1)
+		self.assertEqual(pr.items[0].qty, 1)
+		self.assertEqual(pr.items[0].warehouse, self.warehouse_1)
+		# leftover space was for 500 kg (0.5 Bag)
+		# Since Bag is a whole UOM, 1(out of 2) Bag will be unassigned
+
+		pr.delete()
+		rule_1.delete()
+		rule_2.delete()
+
+	def test_putaway_rules_with_reoccurring_item(self):
+		"""Test rules on same item entered multiple times with different rate."""
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
+			uom="Kg")
+		# total capacity is 200 Kg
+
+		pr = make_purchase_receipt(item_code="_Rice", qty=100, apply_putaway_rule=1,
+			do_not_submit=1)
+		pr.append("items", {
+			"item_code": "_Rice",
+			"warehouse": "_Test Warehouse - _TC",
+			"qty": 200,
+			"uom": "Kg",
+			"stock_uom": "Kg",
+			"stock_qty": 200,
+			"received_qty": 200,
+			"rate": 100,
+			"conversion_factor": 1.0,
+		}) # same item entered again in PR but with different rate
+		pr.save()
+		self.assertEqual(len(pr.items), 2)
+		self.assertEqual(pr.items[0].qty, 100)
+		self.assertEqual(pr.items[0].warehouse, self.warehouse_1)
+		self.assertEqual(pr.items[0].putaway_rule, rule_1.name)
+		# same rule applied to second item row
+		# with previous assignment considered
+		self.assertEqual(pr.items[1].qty, 100) # 100 unassigned in second row from 200
+		self.assertEqual(pr.items[1].warehouse, self.warehouse_1)
+		self.assertEqual(pr.items[1].putaway_rule, rule_1.name)
+
+		pr.delete()
+		rule_1.delete()
+
+	def test_validate_over_receipt_in_warehouse(self):
+		"""Test if overreceipt is blocked in the presence of putaway rules."""
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
+			uom="Kg")
+
+		pr = make_purchase_receipt(item_code="_Rice", qty=300, apply_putaway_rule=1,
+			do_not_submit=1)
+		self.assertEqual(len(pr.items), 1)
+		self.assertEqual(pr.items[0].qty, 200) # 100 is unassigned fro 300 Kg
+		self.assertEqual(pr.items[0].warehouse, self.warehouse_1)
+		self.assertEqual(pr.items[0].putaway_rule, rule_1.name)
+
+		# force overreceipt and disable apply putaway rule in PR
+		pr.items[0].qty = 300
+		pr.items[0].stock_qty = 300
+		pr.apply_putaway_rule = 0
+		self.assertRaises(frappe.ValidationError, pr.save)
+
+		pr.delete()
+		rule_1.delete()
+
+	def test_putaway_rule_on_stock_entry_material_transfer(self):
+		"""Test if source warehouse is considered while applying rules."""
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
+			uom="Kg") # higher priority
+		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=100,
+			uom="Kg", priority=2)
+
+		stock_entry = make_stock_entry(item_code="_Rice", source=self.warehouse_1, qty=200,
+			target="_Test Warehouse - _TC", purpose="Material Transfer",
+			apply_putaway_rule=1, do_not_submit=1)
+
+		stock_entry_item = stock_entry.get("items")[0]
+
+		# since source warehouse is Rack 1, rule 1 (for Rack 1) will be avoided
+		# even though it has more free space and higher priority
+		self.assertEqual(stock_entry_item.t_warehouse, self.warehouse_2)
+		self.assertEqual(stock_entry_item.qty, 100) # unassigned 100 out of 200 Kg
+		self.assertEqual(stock_entry_item.putaway_rule, rule_2.name)
+
+		stock_entry.delete()
+		rule_1.delete()
+		rule_2.delete()
+
+	def test_putaway_rule_on_stock_entry_material_transfer_reoccuring_item(self):
+		"""Test if reoccuring item is correctly considered."""
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=300,
+			uom="Kg")
+		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=600,
+			uom="Kg", priority=2)
+
+		# create SE with first row having source warehouse as Rack 2
+		stock_entry = make_stock_entry(item_code="_Rice", source=self.warehouse_2, qty=200,
+			target="_Test Warehouse - _TC", purpose="Material Transfer",
+			apply_putaway_rule=1, do_not_submit=1)
+
+		# Add rows with source warehouse as Rack 1
+		stock_entry.extend("items", [
+			{
+				"item_code": "_Rice",
+				"s_warehouse": self.warehouse_1,
+				"t_warehouse": "_Test Warehouse - _TC",
+				"qty": 100,
+				"basic_rate": 50,
+				"conversion_factor": 1.0,
+				"transfer_qty": 100
+			},
+			{
+				"item_code": "_Rice",
+				"s_warehouse": self.warehouse_1,
+				"t_warehouse": "_Test Warehouse - _TC",
+				"qty": 200,
+				"basic_rate": 60,
+				"conversion_factor": 1.0,
+				"transfer_qty": 200
+			}
+		])
+
+		stock_entry.save()
+
+		# since source warehouse was Rack 2, exclude rule_2
+		self.assertEqual(stock_entry.items[0].t_warehouse, self.warehouse_1)
+		self.assertEqual(stock_entry.items[0].qty, 200)
+		self.assertEqual(stock_entry.items[0].putaway_rule, rule_1.name)
+
+		# since source warehouse was Rack 1, exclude rule_1 even though it has
+		# higher priority
+		self.assertEqual(stock_entry.items[1].t_warehouse, self.warehouse_2)
+		self.assertEqual(stock_entry.items[1].qty, 100)
+		self.assertEqual(stock_entry.items[1].putaway_rule, rule_2.name)
+
+		self.assertEqual(stock_entry.items[2].t_warehouse, self.warehouse_2)
+		self.assertEqual(stock_entry.items[2].qty, 200)
+		self.assertEqual(stock_entry.items[2].putaway_rule, rule_2.name)
+
+		stock_entry.delete()
+		rule_1.delete()
+		rule_2.delete()
+
+	def test_putaway_rule_on_stock_entry_material_transfer_batch_serial_item(self):
+		"""Test if batch and serial items are split correctly."""
+		if not frappe.db.exists("Item", "Water Bottle"):
+			make_item("Water Bottle", {
+				"is_stock_item": 1,
+				"has_batch_no" : 1,
+				"create_new_batch": 1,
+				"has_serial_no": 1,
+				"serial_no_series": "BOTTL-.####",
+				"stock_uom": "Nos"
+			})
+
+		rule_1 = create_putaway_rule(item_code="Water Bottle", warehouse=self.warehouse_1, capacity=3,
+			uom="Nos")
+		rule_2 = create_putaway_rule(item_code="Water Bottle", warehouse=self.warehouse_2, capacity=2,
+		uom="Nos")
+
+		make_new_batch(batch_id="BOTTL-BATCH-1", item_code="Water Bottle")
+
+		pr = make_purchase_receipt(item_code="Water Bottle", qty=5, do_not_submit=1)
+		pr.items[0].batch_no = "BOTTL-BATCH-1"
+		pr.save()
+		pr.submit()
+
+		serial_nos = frappe.get_list("Serial No", filters={"purchase_document_no": pr.name, "status": "Active"})
+		serial_nos = [d.name for d in serial_nos]
+
+		stock_entry = make_stock_entry(item_code="Water Bottle", source="_Test Warehouse - _TC", qty=5,
+			target="Finished Goods - _TC", purpose="Material Transfer",
+			apply_putaway_rule=1, do_not_save=1)
+		stock_entry.items[0].batch_no = "BOTTL-BATCH-1"
+		stock_entry.items[0].serial_no = "\n".join(serial_nos)
+		stock_entry.save()
+
+		self.assertEqual(stock_entry.items[0].t_warehouse, self.warehouse_1)
+		self.assertEqual(stock_entry.items[0].qty, 3)
+		self.assertEqual(stock_entry.items[0].putaway_rule, rule_1.name)
+		self.assertEqual(stock_entry.items[0].serial_no, "\n".join(serial_nos[:3]))
+		self.assertEqual(stock_entry.items[0].batch_no, "BOTTL-BATCH-1")
+
+		self.assertEqual(stock_entry.items[1].t_warehouse, self.warehouse_2)
+		self.assertEqual(stock_entry.items[1].qty, 2)
+		self.assertEqual(stock_entry.items[1].putaway_rule, rule_2.name)
+		self.assertEqual(stock_entry.items[1].serial_no, "\n".join(serial_nos[3:]))
+		self.assertEqual(stock_entry.items[1].batch_no, "BOTTL-BATCH-1")
+
+		stock_entry.delete()
+		pr.cancel()
+		rule_1.delete()
+		rule_2.delete()
+
+	def test_putaway_rule_on_stock_entry_material_receipt(self):
+		"""Test if rules are applied in Stock Entry of type Receipt."""
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
+			uom="Kg") # more capacity
+		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=100,
+			uom="Kg")
+
+		stock_entry = make_stock_entry(item_code="_Rice", qty=100,
+			target="_Test Warehouse - _TC", purpose="Material Receipt",
+			apply_putaway_rule=1, do_not_submit=1)
+
+		stock_entry_item = stock_entry.get("items")[0]
+
+		self.assertEqual(stock_entry_item.t_warehouse, self.warehouse_1)
+		self.assertEqual(stock_entry_item.qty, 100)
+		self.assertEqual(stock_entry_item.putaway_rule, rule_1.name)
+
+		stock_entry.delete()
+		rule_1.delete()
+		rule_2.delete()
+
+def create_putaway_rule(**args):
+	args = frappe._dict(args)
+	putaway = frappe.new_doc("Putaway Rule")
+
+	putaway.disable = args.disable or 0
+	putaway.company = args.company or "_Test Company"
+	putaway.item_code = args.item or args.item_code or "_Test Item"
+	putaway.warehouse = args.warehouse
+	putaway.priority = args.priority or 1
+	putaway.capacity = args.capacity or 1
+	putaway.stock_uom = frappe.db.get_value("Item", putaway.item_code, "stock_uom")
+	putaway.uom = args.uom or putaway.stock_uom
+	putaway.conversion_factor = get_conversion_factor(putaway.item_code, putaway.uom)['conversion_factor']
+
+	if not args.do_not_save:
+		putaway.save()
+
+	return putaway
\ No newline at end of file
diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.js b/erpnext/stock/doctype/quality_inspection/quality_inspection.js
index 03e3de1..f7565fd 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.js
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.js
@@ -4,6 +4,55 @@
 cur_frm.cscript.refresh = cur_frm.cscript.inspection_type;
 
 frappe.ui.form.on("Quality Inspection", {
+
+	setup: function(frm) {
+		frm.set_query("batch_no", function() {
+			return {
+				filters: {
+					"item": frm.doc.item_code
+				}
+			};
+		});
+
+		// Serial No based on item_code
+		frm.set_query("item_serial_no", function() {
+			let filters = {};
+			if (frm.doc.item_code) {
+				filters = {
+					'item_code': frm.doc.item_code
+				};
+			}
+			return { filters: filters };
+		});
+
+		// item code based on GRN/DN
+		frm.set_query("item_code", function(doc) {
+			let doctype = doc.reference_type;
+
+			if (doc.reference_type !== "Job Card") {
+				doctype = (doc.reference_type == "Stock Entry") ?
+					"Stock Entry Detail" : doc.reference_type + " Item";
+			}
+
+			if (doc.reference_type && doc.reference_name) {
+				let filters = {
+					"from": doctype,
+					"inspection_type": doc.inspection_type
+				};
+
+				if (doc.reference_type == doctype)
+					filters["reference_name"] = doc.reference_name;
+				else
+					filters["parent"] = doc.reference_name;
+
+				return {
+					query: "erpnext.stock.doctype.quality_inspection.quality_inspection.item_query",
+					filters: filters
+				};
+			}
+		});
+	},
+
 	refresh: function(frm) {
 		// Ignore cancellation of reference doctype on cancel all.
 		frm.ignore_doctypes_on_cancel_all = [frm.doc.reference_type];
@@ -31,55 +80,5 @@
 				}
 			});
 		}
-	}
-})
-
-// item code based on GRN/DN
-cur_frm.fields_dict['item_code'].get_query = function(doc, cdt, cdn) {
-	let doctype = doc.reference_type;
-
-	if (doc.reference_type !== "Job Card") {
-		doctype = (doc.reference_type == "Stock Entry") ?
-			"Stock Entry Detail" : doc.reference_type + " Item";
-	}
-
-	if (doc.reference_type && doc.reference_name) {
-		let filters = {
-			"from": doctype,
-			"inspection_type": doc.inspection_type
-		};
-
-		if (doc.reference_type == doctype)
-			filters["reference_name"] = doc.reference_name;
-		else
-			filters["parent"] = doc.reference_name;
-
-		return {
-			query: "erpnext.stock.doctype.quality_inspection.quality_inspection.item_query",
-			filters: filters
-		};
-	}
-},
-
-// Serial No based on item_code
-cur_frm.fields_dict['item_serial_no'].get_query = function(doc, cdt, cdn) {
-	var filters = {};
-	if (doc.item_code) {
-		filters = {
-			'item_code': doc.item_code
-		}
-	}
-	return { filters: filters }
-}
-
-cur_frm.set_query("batch_no", function(doc) {
-	return {
-		filters: {
-			"item": doc.item_code
-		}
-	}
-})
-
-cur_frm.add_fetch('item_code', 'item_name', 'item_name');
-cur_frm.add_fetch('item_code', 'description', 'description');
-
+	},
+});
\ No newline at end of file
diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json
index f6d7619..edfe7e9 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json
@@ -136,6 +136,7 @@
    "width": "50%"
   },
   {
+   "fetch_from": "item_code.item_name",
    "fieldname": "item_name",
    "fieldtype": "Data",
    "in_global_search": 1,
@@ -143,6 +144,7 @@
    "read_only": 1
   },
   {
+   "fetch_from": "item_code.description",
    "fieldname": "description",
    "fieldtype": "Small Text",
    "label": "Description",
@@ -236,7 +238,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-11-19 17:06:05.409963",
+ "modified": "2020-12-18 19:59:55.710300",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Quality Inspection",
diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py
index ae4eb9b..58b1eca 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py
@@ -6,7 +6,7 @@
 from frappe.model.document import Document
 from frappe.model.mapper import get_mapped_doc
 from frappe import _
-from frappe.utils import flt
+from frappe.utils import flt, cint
 from erpnext.stock.doctype.quality_inspection_template.quality_inspection_template \
 	import get_template_details
 
@@ -16,7 +16,7 @@
 			self.get_item_specification_details()
 
 		if self.readings:
-			self.set_status_based_on_acceptance_formula()
+			self.inspect_and_set_status()
 
 	def get_item_specification_details(self):
 		if not self.quality_inspection_template:
@@ -29,9 +29,7 @@
 		parameters = get_template_details(self.quality_inspection_template)
 		for d in parameters:
 			child = self.append('readings', {})
-			child.specification = d.specification
-			child.value = d.value
-			child.acceptance_formula = d.acceptance_formula
+			child.update(d)
 			child.status = "Accepted"
 
 	def get_quality_inspection_template(self):
@@ -69,35 +67,98 @@
 				doctype = 'Stock Entry Detail'
 
 			if self.reference_type and self.reference_name:
+				conditions = ""
+				if self.batch_no and self.docstatus == 1:
+					conditions += " and t1.batch_no = '%s'"%(self.batch_no)
+
+				if self.docstatus == 2: # if cancel, then remove qi link wherever same name
+					conditions += " and t1.quality_inspection = '%s'"%(self.name)
+
 				frappe.db.sql("""
-					UPDATE `tab{child_doc}` t1, `tab{parent_doc}` t2
-					SET t1.quality_inspection = %s, t2.modified = %s
-					WHERE t1.parent = %s and t1.item_code = %s and t1.parent = t2.name
-				""".format(parent_doc=self.reference_type, child_doc=doctype),
+					UPDATE
+						`tab{child_doc}` t1, `tab{parent_doc}` t2
+					SET
+						t1.quality_inspection = %s, t2.modified = %s
+					WHERE
+						t1.parent = %s
+						and t1.item_code = %s
+						and t1.parent = t2.name
+						{conditions}
+				""".format(parent_doc=self.reference_type, child_doc=doctype, conditions=conditions),
 					(quality_inspection, self.modified, self.reference_name, self.item_code))
 
-	def set_status_based_on_acceptance_formula(self):
+	def inspect_and_set_status(self):
 		for reading in self.readings:
-			if not reading.acceptance_formula: continue
+			if not reading.manual_inspection: # dont auto set status if manual
+				if reading.formula_based_criteria:
+					self.set_status_based_on_acceptance_formula(reading)
+				else:
+					# if not formula based check acceptance values set
+					self.set_status_based_on_acceptance_values(reading)
 
-			condition = reading.acceptance_formula
-			data = {}
+	def set_status_based_on_acceptance_values(self, reading):
+		if not cint(reading.numeric):
+			result = reading.get("reading_value") == reading.get("value")
+		else:
+			# numeric readings
+			result = self.min_max_criteria_passed(reading)
+
+		reading.status = "Accepted" if result else "Rejected"
+
+	def min_max_criteria_passed(self, reading):
+		"""Determine whether all readings fall in the acceptable range."""
+		for i in range(1, 11):
+			reading_value = reading.get("reading_" + str(i))
+			if reading_value is not None and reading_value.strip():
+				result = flt(reading.get("min_value")) <= flt(reading_value) <= flt(reading.get("max_value"))
+				if not result: return False
+		return True
+
+	def set_status_based_on_acceptance_formula(self, reading):
+		if not reading.acceptance_formula:
+			frappe.throw(_("Row #{0}: Acceptance Criteria Formula is required.").format(reading.idx),
+				title=_("Missing Formula"))
+
+		condition = reading.acceptance_formula
+		data = self.get_formula_evaluation_data(reading)
+
+		try:
+			result = frappe.safe_eval(condition, None, data)
+			reading.status = "Accepted" if result else "Rejected"
+		except NameError as e:
+			field = frappe.bold(e.args[0].split()[1])
+			frappe.throw(_("Row #{0}: {1} is not a valid reading field. Please refer to the field description.")
+				.format(reading.idx, field),
+				title=_("Invalid Formula"))
+		except Exception:
+			frappe.throw(_("Row #{0}: Acceptance Criteria Formula is incorrect.").format(reading.idx),
+				title=_("Invalid Formula"))
+
+	def get_formula_evaluation_data(self, reading):
+		data = {}
+		if not cint(reading.numeric):
+			data = {"reading_value": reading.get("reading_value")}
+		else:
+			# numeric readings
 			for i in range(1, 11):
 				field = "reading_" + str(i)
-				data[field] = flt(reading.get(field)) or 0
+				data[field] = flt(reading.get(field))
+			data["mean"] = self.calculate_mean(reading)
 
-			try:
-				result = frappe.safe_eval(condition, None, data)
-				reading.status = "Accepted" if result else "Rejected"
-			except SyntaxError:
-				frappe.throw(_("Row #{0}: Acceptance Criteria Formula is incorrect.").format(reading.idx),
-					title=_("Invalid Formula"))
-			except NameError as e:
-				field = frappe.bold(e.args[0].split()[1])
-				frappe.throw(_("Row #{0}: {1} is not a valid reading field. Please refer to the field description.")
-					.format(reading.idx, field),
-					title=_("Invalid Formula"))
+		return data
 
+	def calculate_mean(self, reading):
+		"""Calculate mean of all non-empty readings."""
+		from statistics import mean
+		readings_list = []
+
+		for i in range(1, 11):
+			reading_value = reading.get("reading_" + str(i))
+			if reading_value is not None and reading_value.strip():
+				readings_list.append(flt(reading_value))
+
+		actual_mean = mean(readings_list) if readings_list else 0
+		return actual_mean
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
index 2c40009..a7dfc9e 100644
--- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
@@ -44,24 +44,61 @@
 		qa.delete()
 		dn.delete()
 
+	def test_value_based_qi_readings(self):
+		# Test QI based on acceptance values (Non formula)
+		dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
+		readings = [{
+			"specification": "Iron Content", # numeric reading
+			"min_value": 0.1,
+			"max_value": 0.9,
+			"reading_1": "0.4"
+		},
+		{
+			"specification": "Particle Inspection Needed", # non-numeric reading
+			"numeric": 0,
+			"value": "Yes",
+			"reading_value": "Yes"
+		}]
+
+		qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name,
+			readings=readings, do_not_save=True)
+		qa.save()
+
+		# status must be auto set as per formula
+		self.assertEqual(qa.readings[0].status, "Accepted")
+		self.assertEqual(qa.readings[1].status, "Accepted")
+
+		qa.delete()
+		dn.delete()
+
 	def test_formula_based_qi_readings(self):
 		dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
 		readings = [{
-			"specification": "Iron Content",
+			"specification": "Iron Content", # numeric reading
+			"formula_based_criteria": 1,
 			"acceptance_formula": "reading_1 > 0.35 and reading_1 < 0.50",
-			"reading_1": 0.4
+			"reading_1": "0.4"
 		},
 		{
-			"specification": "Calcium Content",
+			"specification": "Calcium Content", # numeric reading
+			"formula_based_criteria": 1,
 			"acceptance_formula": "reading_1 > 0.20 and reading_1 < 0.50",
-			"reading_1": 0.7
+			"reading_1": "0.7"
 		},
 		{
-			"specification": "Mg Content",
-			"acceptance_formula": "(reading_1 + reading_2 + reading_3) / 3 < 0.9",
-			"reading_1": 0.5,
-			"reading_2": 0.7,
+			"specification": "Mg Content", # numeric reading
+			"formula_based_criteria": 1,
+			"acceptance_formula": "mean < 0.9",
+			"reading_1": "0.5",
+			"reading_2": "0.7",
 			"reading_3": "random text" # check if random string input causes issues
+		},
+		{
+			"specification": "Calcium Content", # non-numeric reading
+			"formula_based_criteria": 1,
+			"numeric": 0,
+			"acceptance_formula": "reading_value in ('Grade A', 'Grade B', 'Grade C')",
+			"reading_value": "Grade B"
 		}]
 
 		qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name,
@@ -72,6 +109,7 @@
 		self.assertEqual(qa.readings[0].status, "Accepted")
 		self.assertEqual(qa.readings[1].status, "Rejected")
 		self.assertEqual(qa.readings[2].status, "Accepted")
+		self.assertEqual(qa.readings[3].status, "Accepted")
 
 		qa.delete()
 		dn.delete()
@@ -86,11 +124,20 @@
 	qa.item_code = args.item_code or "_Test Item with QA"
 	qa.sample_size = 1
 	qa.inspected_by = frappe.session.user
+	qa.status = args.status or "Accepted"
 
-	readings = args.readings or {"specification": "Size", "status": args.status}
+	if not args.readings:
+		create_quality_inspection_parameter("Size")
+		readings = {"specification": "Size", "min_value": 0, "max_value": 10}
+	else:
+		readings = args.readings
+
+	if args.status == "Rejected":
+		readings["reading_1"] = "12" # status is auto set in child on save
 
 	if isinstance(readings, list):
 		for entry in readings:
+			create_quality_inspection_parameter(entry["specification"])
 			qa.append("readings", entry)
 	else:
 		qa.append("readings", readings)
@@ -101,3 +148,11 @@
 			qa.submit()
 
 	return qa
+
+def create_quality_inspection_parameter(parameter):
+	if not frappe.db.exists("Quality Inspection Parameter", parameter):
+		frappe.get_doc({
+			"doctype": "Quality Inspection Parameter",
+			"parameter": parameter,
+			"description": parameter
+		}).insert()
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/stock/doctype/quality_inspection_parameter/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/stock/doctype/quality_inspection_parameter/__init__.py
diff --git a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.js b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.js
new file mode 100644
index 0000000..47c7e11
--- /dev/null
+++ b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.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('Quality Inspection Parameter', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json
new file mode 100644
index 0000000..0b5a9b5
--- /dev/null
+++ b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json
@@ -0,0 +1,86 @@
+{
+ "actions": [],
+ "autoname": "field:parameter",
+ "creation": "2020-12-28 17:06:00.254129",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "parameter",
+  "description"
+ ],
+ "fields": [
+  {
+   "fieldname": "parameter",
+   "fieldtype": "Data",
+   "label": "Parameter",
+   "unique": 1
+  },
+  {
+   "fieldname": "description",
+   "fieldtype": "Text Editor",
+   "label": "Description"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2020-12-28 18:06:54.897317",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Quality Inspection Parameter",
+ "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": "Stock User",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Quality Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Manufacturing User",
+   "share": 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/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.py b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.py
new file mode 100644
index 0000000..8678422
--- /dev/null
+++ b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.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 QualityInspectionParameter(Document):
+	pass
diff --git a/erpnext/non_profit/doctype/membership_settings/test_membership_settings.py b/erpnext/stock/doctype/quality_inspection_parameter/test_quality_inspection_parameter.py
similarity index 76%
copy from erpnext/non_profit/doctype/membership_settings/test_membership_settings.py
copy to erpnext/stock/doctype/quality_inspection_parameter/test_quality_inspection_parameter.py
index 2ad7984..cefdc08 100644
--- a/erpnext/non_profit/doctype/membership_settings/test_membership_settings.py
+++ b/erpnext/stock/doctype/quality_inspection_parameter/test_quality_inspection_parameter.py
@@ -6,5 +6,5 @@
 # import frappe
 import unittest
 
-class TestMembershipSettings(unittest.TestCase):
+class TestQualityInspectionParameter(unittest.TestCase):
 	pass
diff --git a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json
index c1976dd..35d58ef 100644
--- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json
+++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json
@@ -7,21 +7,28 @@
  "engine": "InnoDB",
  "field_order": [
   "specification",
-  "value",
   "status",
+  "value",
+  "numeric",
+  "manual_inspection",
   "column_break_4",
+  "min_value",
+  "max_value",
+  "formula_based_criteria",
   "acceptance_formula",
   "section_break_3",
+  "reading_value",
+  "section_break_14",
   "reading_1",
   "reading_2",
   "reading_3",
-  "column_break_10",
   "reading_4",
+  "column_break_10",
   "reading_5",
   "reading_6",
-  "column_break_14",
   "reading_7",
   "reading_8",
+  "column_break_14",
   "reading_9",
   "reading_10"
  ],
@@ -29,24 +36,25 @@
   {
    "columns": 3,
    "fieldname": "specification",
-   "fieldtype": "Data",
+   "fieldtype": "Link",
    "in_list_view": 1,
    "label": "Parameter",
    "oldfieldname": "specification",
    "oldfieldtype": "Data",
+   "options": "Quality Inspection Parameter",
    "reqd": 1
   },
   {
    "columns": 2,
+   "depends_on": "eval:(!doc.formula_based_criteria && !doc.numeric)",
    "fieldname": "value",
    "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Acceptance Criteria",
+   "label": "Acceptance Criteria Value",
    "oldfieldname": "value",
    "oldfieldtype": "Data"
   },
   {
-   "columns": 1,
+   "columns": 2,
    "fieldname": "reading_1",
    "fieldtype": "Data",
    "in_list_view": 1,
@@ -58,7 +66,6 @@
    "columns": 1,
    "fieldname": "reading_2",
    "fieldtype": "Data",
-   "in_list_view": 1,
    "label": "Reading 2",
    "oldfieldname": "reading_2",
    "oldfieldtype": "Data"
@@ -67,7 +74,6 @@
    "columns": 1,
    "fieldname": "reading_3",
    "fieldtype": "Data",
-   "in_list_view": 1,
    "label": "Reading 3",
    "oldfieldname": "reading_3",
    "oldfieldtype": "Data"
@@ -130,18 +136,21 @@
    "label": "Status",
    "oldfieldname": "status",
    "oldfieldtype": "Select",
-   "options": "Accepted\nRejected"
+   "options": "\nAccepted\nRejected"
   },
   {
+   "depends_on": "eval:!doc.numeric",
    "fieldname": "section_break_3",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "label": "Value Based Inspection"
   },
   {
    "fieldname": "column_break_4",
    "fieldtype": "Column Break"
   },
   {
-   "description": "Simple Python formula based on numeric Readings.<br> Example 1: <b>reading_1 &gt; 0.2 and reading_1 &lt; 0.5</b><br>\nExample 2: <b>(reading_1 + reading_2) / 2 &lt; 10</b>",
+   "depends_on": "formula_based_criteria",
+   "description": "Simple Python formula applied on Reading fields.<br> Numeric eg. 1: <b>reading_1 &gt; 0.2 and reading_1 &lt; 0.5</b><br>\nNumeric eg. 2: <b>mean &gt; 3.5</b> (mean of populated fields)<br>\nValue based eg.:  <b>reading_value in (\"A\", \"B\", \"C\")</b>",
    "fieldname": "acceptance_formula",
    "fieldtype": "Code",
    "label": "Acceptance Criteria Formula"
@@ -153,12 +162,60 @@
   {
    "fieldname": "column_break_14",
    "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "formula_based_criteria",
+   "fieldtype": "Check",
+   "label": "Formula Based Criteria"
+  },
+  {
+   "depends_on": "eval:(!doc.formula_based_criteria && doc.numeric)",
+   "description": "Applied on each reading.",
+   "fieldname": "min_value",
+   "fieldtype": "Float",
+   "label": "Minimum Value"
+  },
+  {
+   "depends_on": "eval:(!doc.formula_based_criteria && doc.numeric)",
+   "description": "Applied on each reading.",
+   "fieldname": "max_value",
+   "fieldtype": "Float",
+   "label": "Maximum Value"
+  },
+  {
+   "columns": 2,
+   "depends_on": "eval:!doc.numeric",
+   "fieldname": "reading_value",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Reading Value"
+  },
+  {
+   "depends_on": "numeric",
+   "fieldname": "section_break_14",
+   "fieldtype": "Section Break",
+   "label": "Numeric Inspection"
+  },
+  {
+   "default": "0",
+   "description": "Set the status manually.",
+   "fieldname": "manual_inspection",
+   "fieldtype": "Check",
+   "label": "Manual Inspection"
+  },
+  {
+   "default": "1",
+   "fieldname": "numeric",
+   "fieldtype": "Check",
+   "in_list_view": 1,
+   "label": "Numeric"
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-11-16 16:34:29.947856",
+ "modified": "2021-02-01 19:46:22.138018",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Quality Inspection Reading",
diff --git a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py
index e284846..01d2031 100644
--- a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py
+++ b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py
@@ -13,6 +13,7 @@
 	if not template: return []
 
 	return frappe.get_all('Item Quality Inspection Parameter',
-		fields=["specification", "value", "acceptance_formula"],
+		fields=["specification", "value", "acceptance_formula",
+			"numeric", "formula_based_criteria", "min_value", "max_value"],
 		filters={'parenttype': 'Quality Inspection Template', 'parent': template},
 		order_by="idx")
\ No newline at end of file
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js
index e429cd5..b3e4286 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js
@@ -31,7 +31,7 @@
 		}
 	},
 	refresh: function(frm) {
-		if (frm.doc.status == "Failed") {
+		if (frm.doc.status == "Failed" && frm.doc.docstatus==1) {
 			frm.add_custom_button(__('Restart'), function () {
 				frm.trigger("restart_reposting");
 			}).addClass("btn-primary");
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index ba2c2c6..8436acb 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -46,6 +46,9 @@
 
 def repost(doc):
 	try:
+		if not frappe.db.exists("Repost Item Valuation", doc.name):
+			return
+
 		doc.set_status('In Progress')
 		frappe.db.commit()
 
@@ -64,7 +67,7 @@
 			message += "<br>" + "Traceback: <br>" + traceback
 		frappe.db.set_value(doc.doctype, doc.name, 'error_log', message)
 
-		notify_error_to_stock_managers(doc)
+		notify_error_to_stock_managers(doc, message)
 		doc.set_status('Failed')
 		raise
 	finally:
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index 2d89996..e65913b 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -6,7 +6,7 @@
 import json
 
 from frappe.model.naming import make_autoname
-from frappe.utils import cint, cstr, flt, add_days, nowdate, getdate
+from frappe.utils import cint, cstr, flt, add_days, nowdate, getdate, get_link_to_form
 from erpnext.stock.get_item_details import get_reserved_qty_for_so
 
 from frappe import _, ValidationError
@@ -241,7 +241,7 @@
 			for serial_no in serial_nos:
 				if frappe.db.exists("Serial No", serial_no):
 					sr = frappe.db.get_value("Serial No", serial_no, ["name", "item_code", "batch_no", "sales_order",
-						"delivery_document_no", "delivery_document_type", "warehouse",
+						"delivery_document_no", "delivery_document_type", "warehouse", "purchase_document_type",
 						"purchase_document_no", "company"], as_dict=1)
 
 					if sr.item_code!=sle.item_code:
@@ -249,9 +249,10 @@
 							frappe.throw(_("Serial No {0} does not belong to Item {1}").format(serial_no,
 								sle.item_code), SerialNoItemError)
 
-					if cint(sle.actual_qty) > 0 and has_duplicate_serial_no(sr, sle):
-						frappe.throw(_("Serial No {0} has already been received").format(serial_no),
-							SerialNoDuplicateError)
+					if cint(sle.actual_qty) > 0 and has_serial_no_exists(sr, sle):
+						doc_name = frappe.bold(get_link_to_form(sr.purchase_document_type, sr.purchase_document_no))
+						frappe.throw(_("Serial No {0} has already been received in the {1} #{2}")
+							.format(frappe.bold(serial_no), sr.purchase_document_type, doc_name), SerialNoDuplicateError)
 
 					if (sr.delivery_document_no and sle.voucher_type not in ['Stock Entry', 'Stock Reconciliation']
 						and sle.voucher_type == sr.delivery_document_type):
@@ -348,7 +349,7 @@
 		frappe.throw(_("""{0} Serial No {1} cannot be delivered""")
 			.format(msg, sr.name))
 
-def has_duplicate_serial_no(sn, sle):
+def has_serial_no_exists(sn, sle):
 	if (sn.warehouse and not sle.skip_serial_no_validaiton
 		and sle.voucher_type != 'Stock Reconciliation'):
 		return True
@@ -358,12 +359,13 @@
 
 	status = False
 	if sn.purchase_document_no:
-		if sle.voucher_type in ['Purchase Receipt', 'Stock Entry', "Purchase Invoice"] and \
-			sn.delivery_document_type not in ['Purchase Receipt', 'Stock Entry', "Purchase Invoice"]:
+		if (sle.voucher_type in ['Purchase Receipt', 'Stock Entry', "Purchase Invoice"] and
+			sn.delivery_document_type not in ['Purchase Receipt', 'Stock Entry', "Purchase Invoice"]):
 			status = True
 
-		if status and sle.voucher_type == 'Stock Entry' and \
-			frappe.db.get_value('Stock Entry', sle.voucher_no, 'purpose') != 'Material Receipt':
+		# If status is receipt then system will allow to in-ward the delivered serial no
+		if (status and sle.voucher_type == "Stock Entry" and frappe.db.get_value("Stock Entry",
+			sle.voucher_no, "purpose") in ("Material Receipt", "Material Transfer")):
 			status = False
 
 	return status
@@ -419,7 +421,7 @@
 		if is_new:
 			created_numbers.append(sr.name)
 
-	form_links = list(map(lambda d: frappe.utils.get_link_to_form('Serial No', d), created_numbers))
+	form_links = list(map(lambda d: get_link_to_form('Serial No', d), created_numbers))
 
 	# Setting up tranlated title field for all cases
 	singular_title = _("Serial Number Created")
diff --git a/erpnext/stock/doctype/shipment/test_shipment.py b/erpnext/stock/doctype/shipment/test_shipment.py
index e1fa207..9c3e22f 100644
--- a/erpnext/stock/doctype/shipment/test_shipment.py
+++ b/erpnext/stock/doctype/shipment/test_shipment.py
@@ -190,6 +190,7 @@
 	company.abbr = abbr
 	company.default_currency = 'EUR'
 	company.country = 'Germany'
+	company.enable_perpetual_inventory = 0
 	company.insert()
 	return company
 
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 98116ec..274bbc2 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -1,9 +1,20 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt
 
 frappe.provide("erpnext.stock");
+frappe.provide("erpnext.accounts.dimensions");
+
+{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %};
 
 frappe.ui.form.on('Stock Entry', {
 	setup: function(frm) {
+		frm.set_indicator_formatter('item_code', function(doc) {
+			if (!doc.s_warehouse) {
+				return 'blue';
+			} else {
+				return (doc.qty<=doc.actual_qty) ? 'green' : 'orange';
+			}
+		});
+
 		frm.set_query('work_order', function() {
 			return {
 				filters: [
@@ -86,17 +97,9 @@
 			}
 		});
 
-		frm.set_query("expense_account", "additional_costs", function() {
-			return {
-				query: "erpnext.controllers.queries.tax_account_query",
-				filters: {
-					"account_type": ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"],
-					"company": frm.doc.company
-				}
-			};
-		});
 
 		frm.add_fetch("bom_no", "inspection_required", "inspection_required");
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
 	},
 
 	setup_quality_inspection: function(frm) {
@@ -312,6 +315,8 @@
 				frm.set_value("letter_head", company_doc.default_letter_head);
 			}
 			frm.trigger("toggle_display_account_head");
+
+			erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
 		}
 	},
 
@@ -524,7 +529,6 @@
 				})
 			);
 		}
-		
 		for (let i in frm.doc.items) {
 			let item = frm.doc.items[i];
 
@@ -547,7 +551,7 @@
 
 	calculate_total_additional_costs: function(frm) {
 		const total_additional_costs = frappe.utils.sum(
-			(frm.doc.additional_costs || []).map(function(c) { return flt(c.amount); })
+			(frm.doc.additional_costs || []).map(function(c) { return flt(c.base_amount); })
 		);
 
 		frm.set_value("total_additional_costs",
@@ -580,8 +584,12 @@
 				}
 			});
 		}
+	},
+
+	apply_putaway_rule: function (frm) {
+		if (frm.doc.apply_putaway_rule) erpnext.apply_putaway_rule(frm, frm.doc.purpose);
 	}
-})
+});
 
 frappe.ui.form.on('Stock Entry Detail', {
 	qty: function(frm, cdt, cdn) {
@@ -675,7 +683,13 @@
 						});
 						refresh_field("items");
 
-						if (!d.serial_no) {
+						let no_batch_serial_number_value = !d.serial_no;
+						if (d.has_batch_no && !d.has_serial_no) {
+							// check only batch_no for batched item
+							no_batch_serial_number_value = !d.batch_no;
+						}
+
+						if (no_batch_serial_number_value) {
 							erpnext.stock.select_batch_and_serial_no(frm, d);
 						}
 					}
@@ -716,8 +730,18 @@
 };
 
 frappe.ui.form.on('Landed Cost Taxes and Charges', {
-	amount: function(frm) {
-		frm.events.calculate_amount(frm);
+	amount: function(frm, cdt, cdn) {
+		frm.events.set_base_amount(frm, cdt, cdn);
+
+		// Adding this check because same table in used in LCV
+		// This causes an error if you try to post an LCV immediately after a Stock Entry
+		if (frm.doc.doctype == 'Stock Entry') {
+			frm.events.calculate_amount(frm);
+		}
+	},
+
+	expense_account: function(frm, cdt, cdn) {
+		frm.events.set_account_currency(frm, cdt, cdn);
 	}
 });
 
@@ -765,15 +789,6 @@
 			}
 		}
 
-		this.frm.set_indicator_formatter('item_code',
-			function(doc) {
-				if (!doc.s_warehouse) {
-					return 'blue';
-				} else {
-					return (doc.qty<=doc.actual_qty) ? "green" : "orange"
-				}
-			})
-
 		this.frm.add_fetch("purchase_order", "supplier", "supplier");
 
 		frappe.dynamic_link = { doc: this.frm.doc, fieldname: 'supplier', doctype: 'Supplier' }
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json
index 5aed081..98c047a 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.json
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -27,6 +27,7 @@
   "set_posting_time",
   "inspection_required",
   "from_bom",
+  "apply_putaway_rule",
   "sb1",
   "bom_no",
   "fg_completed_qty",
@@ -640,6 +641,13 @@
    "fieldtype": "Check",
    "label": "Add to Transit",
    "no_copy": 1
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:in_list([\"Material Transfer\", \"Material Receipt\"], doc.purpose)",
+   "fieldname": "apply_putaway_rule",
+   "fieldtype": "Check",
+   "label": "Apply Putaway Rule"
   }
  ],
  "icon": "fa fa-file-text",
@@ -647,7 +655,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-09-09 12:59:02.508943",
+ "modified": "2020-12-09 14:58:13.267321",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 60758b4..9cdc3cf 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -19,6 +19,7 @@
 from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit, get_serial_nos
 from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import OpeningEntryAccountError
 from erpnext.accounts.general_ledger import process_gl_map
+from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals
 import json
 
 from six import string_types, itervalues, iteritems
@@ -42,6 +43,14 @@
 		for item in self.get("items"):
 			item.update(get_bin_details(item.item_code, item.s_warehouse))
 
+	def before_validate(self):
+		from erpnext.stock.doctype.putaway_rule.putaway_rule import apply_putaway_rule
+		apply_rule = self.apply_putaway_rule and (self.purpose in ["Material Transfer", "Material Receipt"])
+
+		if self.get("items") and apply_rule:
+			apply_putaway_rule(self.doctype, self.get("items"), self.company,
+				purpose=self.purpose)
+
 	def validate(self):
 		self.pro_doc = frappe._dict()
 		if self.work_order:
@@ -79,6 +88,7 @@
 		self.validate_serialized_batch()
 		self.set_actual_qty()
 		self.calculate_rate_and_amount()
+		self.validate_putaway_capacity()
 
 	def on_submit(self):
 		self.update_stock_ledger()
@@ -153,7 +163,7 @@
 		if self.purpose not in valid_purposes:
 			frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes)))
 
-		if self.job_card and self.purpose != 'Material Transfer for Manufacture':
+		if self.job_card and self.purpose not in ['Material Transfer for Manufacture', 'Repack']:
 			frappe.throw(_("For job card {0}, you can only make the 'Material Transfer for Manufacture' type stock entry")
 				.format(self.job_card))
 
@@ -186,7 +196,7 @@
 					and (sed.t_warehouse is null or sed.t_warehouse = '')""", self.project, as_list=1)
 
 			amount = amount[0][0] if amount else 0
-			additional_costs = frappe.db.sql(""" select ifnull(sum(sed.amount), 0)
+			additional_costs = frappe.db.sql(""" select ifnull(sum(sed.base_amount), 0)
 				from
 					`tabStock Entry` se, `tabLanded Cost Taxes and Charges` sed
 				where
@@ -259,11 +269,16 @@
 								item_code.append(item.item_code)
 
 	def validate_fg_completed_qty(self):
+		item_wise_qty = {}
 		if self.purpose == "Manufacture" and self.work_order:
 			for d in self.items:
-				if d.is_finished_item and d.qty != self.fg_completed_qty:
-					frappe.throw(_("Finished product quantity <b>{0}</b> and For Quantity <b>{1}</b> cannot be different")
-						.format(d.qty, self.fg_completed_qty))
+				if d.is_finished_item:
+					item_wise_qty.setdefault(d.item_code, []).append(d.qty)
+
+		for item_code, qty_list in iteritems(item_wise_qty):
+			if self.fg_completed_qty != sum(qty_list):
+				frappe.throw(_("The finished product {0} quantity {1} and For Quantity {2} cannot be different")
+					.format(frappe.bold(item_code), frappe.bold(sum(qty_list)), frappe.bold(self.fg_completed_qty)))
 
 	def validate_difference_account(self):
 		if not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
@@ -319,7 +334,7 @@
 
 			if self.purpose == "Manufacture":
 				if validate_for_manufacture:
-					if d.bom_no:
+					if d.is_finished_item or d.is_scrap_item:
 						d.s_warehouse = None
 						if not d.t_warehouse:
 							frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx))
@@ -431,6 +446,7 @@
 
 	def calculate_rate_and_amount(self, reset_outgoing_rate=True, raise_error_if_no_rate=True):
 		self.set_basic_rate(reset_outgoing_rate, raise_error_if_no_rate)
+		init_landed_taxes_and_totals(self)
 		self.distribute_additional_costs()
 		self.update_valuation_rate()
 		self.set_total_incoming_outgoing_value()
@@ -512,14 +528,14 @@
 			bom_items = self.get_bom_raw_materials(finished_item_qty)
 			outgoing_items_cost = sum([flt(row.qty)*flt(row.rate) for row in bom_items.values()])
 
-		return flt(outgoing_items_cost - scrap_items_cost)
+		return flt((outgoing_items_cost - scrap_items_cost) / finished_item_qty)
 
 	def distribute_additional_costs(self):
 		# If no incoming items, set additional costs blank
 		if not any([d.item_code for d in self.items if d.t_warehouse]):
 			self.additional_costs = []
 
-		self.total_additional_costs = sum([flt(t.amount) for t in self.get("additional_costs")])
+		self.total_additional_costs = sum([flt(t.base_amount) for t in self.get("additional_costs")])
 
 		if self.purpose in ("Repack", "Manufacture"):
 			incoming_items_cost = sum([flt(t.basic_amount) for t in self.get("items") if t.is_finished_item])
@@ -699,7 +715,7 @@
 
 		# SLE for target warehouse
 		self.get_sle_for_target_warehouse(sl_entries, finished_item_row)
-		
+
 		# reverse sl entries if cancel
 		if self.docstatus == 2:
 			sl_entries.reverse()
@@ -727,9 +743,9 @@
 					sle.dependant_sle_voucher_detail_no = d.name
 				elif finished_item_row and (finished_item_row.item_code != d.item_code or finished_item_row.t_warehouse != d.s_warehouse):
 					sle.dependant_sle_voucher_detail_no = finished_item_row.name
-					
+
 				sl_entries.append(sle)
-	
+
 	def get_sle_for_target_warehouse(self, sl_entries, finished_item_row):
 		for d in self.get('items'):
 			if cstr(d.t_warehouse):
@@ -759,13 +775,19 @@
 			for d in self.get("items"):
 				if d.t_warehouse:
 					item_account_wise_additional_cost.setdefault((d.item_code, d.name), {})
-					item_account_wise_additional_cost[(d.item_code, d.name)].setdefault(t.expense_account, 0.0)
+					item_account_wise_additional_cost[(d.item_code, d.name)].setdefault(t.expense_account, {
+						"amount": 0.0,
+						"base_amount": 0.0
+					})
 
 					multiply_based_on = d.basic_amount if total_basic_amount else d.qty
 
-					item_account_wise_additional_cost[(d.item_code, d.name)][t.expense_account] += \
+					item_account_wise_additional_cost[(d.item_code, d.name)][t.expense_account]["amount"] += \
 						flt(t.amount * multiply_based_on) / divide_based_on
 
+					item_account_wise_additional_cost[(d.item_code, d.name)][t.expense_account]["base_amount"] += \
+						flt(t.base_amount * multiply_based_on) / divide_based_on
+
 		if item_account_wise_additional_cost:
 			for d in self.get("items"):
 				for account, amount in iteritems(item_account_wise_additional_cost.get((d.item_code, d.name), {})):
@@ -776,7 +798,8 @@
 						"against": d.expense_account,
 						"cost_center": d.cost_center,
 						"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-						"credit": amount
+						"credit_in_account_currency": flt(amount["amount"]),
+						"credit": flt(amount["base_amount"])
 					}, item=d))
 
 					gl_entries.append(self.get_gl_dict({
@@ -784,7 +807,7 @@
 						"against": account,
 						"cost_center": d.cost_center,
 						"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-						"credit": -1 * amount # put it as negative credit instead of debit purposefully
+						"credit": -1 * amount['base_amount'] # put it as negative credit instead of debit purposefully
 					}, item=d))
 
 		return process_gl_map(gl_entries)
@@ -800,6 +823,7 @@
 		if self.job_card:
 			job_doc = frappe.get_doc('Job Card', self.job_card)
 			job_doc.set_transferred_qty(update_status=True)
+			job_doc.set_transferred_qty_in_job_card(self)
 
 		if self.work_order:
 			pro_doc = frappe.get_doc("Work Order", self.work_order)
@@ -1177,7 +1201,10 @@
 							else:
 								qty = (req_qty_each * flt(self.fg_completed_qty)) - remaining_qty
 					else:
-						qty = req_qty_each * flt(self.fg_completed_qty)
+						if self.flags.backflush_based_on == "Material Transferred for Manufacture":
+							qty = (item.qty/trans_qty) * flt(self.fg_completed_qty)
+						else:
+							qty = req_qty_each * flt(self.fg_completed_qty)
 
 			elif backflushed_materials.get(item.item_code):
 				for d in backflushed_materials.get(item.item_code):
@@ -1185,7 +1212,8 @@
 						if (qty > req_qty):
 							qty = (qty/trans_qty) * flt(self.fg_completed_qty)
 
-						if consumed_qty:
+						if consumed_qty and frappe.db.get_single_value("Manufacturing Settings",
+							"material_consumption"):
 							qty -= consumed_qty
 
 			if cint(frappe.get_cached_value('UOM', item.stock_uom, 'must_be_whole_number')):
@@ -1328,9 +1356,6 @@
 						frappe.MappingMismatchError)
 				elif self.purpose == "Material Transfer" and self.add_to_transit:
 					continue
-				elif mreq_item.warehouse != (item.s_warehouse if self.purpose == "Material Issue" else item.t_warehouse):
-					frappe.throw(_("Warehouse for row {0} does not match Material Request").format(item.idx),
-						frappe.MappingMismatchError)
 
 	def validate_batch(self):
 		if self.purpose in ["Material Transfer for Manufacture", "Manufacture", "Repack", "Send to Subcontractor"]:
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
index b78c6be..b12a854 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
@@ -53,6 +53,8 @@
 		args.target = args.to_warehouse
 	if args.item_code:
 		args.item = args.item_code
+	if args.apply_putaway_rule:
+		s.apply_putaway_rule = args.apply_putaway_rule
 
 	if isinstance(args.qty, string_types):
 		if '.' in args.qty:
@@ -118,7 +120,8 @@
 		"t_warehouse": args.target,
 		"qty": args.qty,
 		"basic_rate": args.rate or args.basic_rate,
-		"conversion_factor": 1.0,
+		"conversion_factor": args.conversion_factor or 1.0,
+		"transfer_qty": flt(args.qty) * (flt(args.conversion_factor) or 1.0),
 		"serial_no": args.serial_no,
 		'batch_no': args.batch_no,
 		'cost_center': args.cost_center,
diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
index b78ae6d..864ff48 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "autoname": "hash",
  "creation": "2013-03-29 18:22:12",
  "doctype": "DocType",
@@ -65,9 +66,11 @@
   "against_stock_entry",
   "ste_detail",
   "po_detail",
+  "putaway_rule",
   "column_break_51",
   "reference_purchase_receipt",
-  "quality_inspection"
+  "quality_inspection",
+  "job_card_item"
  ],
  "fields": [
   {
@@ -496,6 +499,16 @@
    "label": "Set Basic Rate Manually"
   },
   {
+   "depends_on": "eval:in_list([\"Material Transfer\", \"Material Receipt\"], parent.purpose)",
+   "fieldname": "putaway_rule",
+   "fieldtype": "Link",
+   "label": "Putaway Rule",
+   "no_copy": 1,
+   "options": "Putaway Rule",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
    "fieldname": "quantity_section",
    "fieldtype": "Section Break",
    "label": "Quantity"
@@ -520,13 +533,22 @@
    "fieldname": "is_finished_item",
    "fieldtype": "Check",
    "label": "Is Finished Item"
+  },
+  {
+   "fieldname": "job_card_item",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Job Card Item",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-12-23 17:55:03.384138",
+ "modified": "2021-02-11 13:47:50.158754",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry Detail",
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index a5c303c..78457e4 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -37,6 +37,7 @@
 		self.block_transactions_against_group_warehouse()
 		self.validate_with_last_transaction_posting_time()
 
+
 	def on_submit(self):
 		self.check_stock_frozen_date()
 		self.actual_amt_check()
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
index e2121fc..ac4ed5e 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -2,6 +2,7 @@
 // License: GNU General Public License v3. See license.txt
 
 frappe.provide("erpnext.stock");
+frappe.provide("erpnext.accounts.dimensions");
 
 frappe.ui.form.on("Stock Reconciliation", {
 	onload: function(frm) {
@@ -26,6 +27,12 @@
 		if (!frm.doc.expense_account) {
 			frm.trigger("set_expense_account");
 		}
+
+		erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
+	},
+
+	company: function(frm) {
+		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
 	},
 
 	refresh: function(frm) {
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 5b40292..f0a90f9 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -30,6 +30,7 @@
 		self.validate_data()
 		self.validate_expense_account()
 		self.set_total_qty_and_amount()
+		self.validate_putaway_capacity()
 
 		if self._action=="submit":
 			self.make_batches('warehouse')
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json
index 859aea2..84af57b 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.json
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.json
@@ -16,6 +16,7 @@
   "action_if_quality_inspection_is_not_submitted",
   "show_barcode_field",
   "clean_description_html",
+  "disable_serial_no_and_batch_selector",
   "section_break_7",
   "auto_insert_price_list_rate_if_missing",
   "allow_negative_stock",
@@ -217,7 +218,7 @@
    "fieldname": "role_allowed_to_create_edit_back_dated_transactions",
    "fieldtype": "Link",
    "label": "Role Allowed to Create/Edit Back-dated Transactions",
-   "options": "User"
+   "options": "Role"
   },
   {
    "fieldname": "column_break_26",
@@ -227,6 +228,12 @@
    "fieldname": "control_historical_stock_transactions_section",
    "fieldtype": "Section Break",
    "label": "Control Historical Stock Transactions"
+  },
+  {
+   "default": "0",
+   "fieldname": "disable_serial_no_and_batch_selector",
+   "fieldtype": "Check",
+   "label": "Disable Serial No And Batch Selector"
   }
  ],
  "icon": "icon-cog",
@@ -234,7 +241,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2020-11-23 22:26:54.225608",
+ "modified": "2021-01-18 13:15:38.352796",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Settings",
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 08f7a83..873cfec 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -19,7 +19,7 @@
 
 from six import string_types, iteritems
 
-sales_doctypes = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']
+sales_doctypes = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice', 'POS Invoice']
 purchase_doctypes = ['Material Request', 'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
 
 @frappe.whitelist()
@@ -74,7 +74,9 @@
 
 	update_party_blanket_order(args, out)
 
-	get_price_list_rate(args, item, out)
+	if not doc or cint(doc.get('is_return')) == 0:
+		# get price list rate only if the invoice is not a credit or debit note
+		get_price_list_rate(args, item, out)
 
 	if args.customer and cint(args.is_pos):
 		out.update(get_pos_profile_item_details(args.company, args))
@@ -672,6 +674,8 @@
 		and price_list=%(price_list)s
 		and ifnull(uom, '') in ('', %(uom)s)"""
 
+	conditions += "and ifnull(batch_no, '') in ('', %(batch_no)s)"
+
 	if not ignore_party:
 		if args.get("customer"):
 			conditions += " and customer=%(customer)s"
@@ -690,7 +694,7 @@
 
 	return frappe.db.sql(""" select name, price_list_rate, uom
 		from `tabItem Price` {conditions}
-		order by valid_from desc, uom desc """.format(conditions=conditions), args)
+		order by valid_from desc, batch_no desc, uom desc """.format(conditions=conditions), args)
 
 def get_price_list_rate_for(args, item_code):
 	"""
@@ -709,6 +713,7 @@
 			"uom": args.get('uom'),
 			"transaction_date": args.get('transaction_date'),
 			"posting_date": args.get('posting_date'),
+			"batch_no": args.get('batch_no')
 	}
 
 	item_price_data = 0
diff --git a/erpnext/stock/landed_taxes_and_charges_common.js b/erpnext/stock/landed_taxes_and_charges_common.js
new file mode 100644
index 0000000..f3f6196
--- /dev/null
+++ b/erpnext/stock/landed_taxes_and_charges_common.js
@@ -0,0 +1,62 @@
+let document_list = ['Landed Cost Voucher', 'Stock Entry'];
+
+document_list.forEach((doctype) => {
+	frappe.ui.form.on(doctype, {
+		refresh: function(frm) {
+			let tax_field = frm.doc.doctype == 'Landed Cost Voucher' ? 'taxes' : 'additional_costs';
+			frm.set_query("expense_account", tax_field, function() {
+				return {
+					filters: {
+						"account_type": ['in', ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"]],
+						"company": frm.doc.company
+					}
+				};
+			});
+		},
+
+		set_account_currency: function(frm, cdt, cdn) {
+			let row = locals[cdt][cdn];
+			if (row.expense_account) {
+				frappe.db.get_value('Account', row.expense_account, 'account_currency', function(value) {
+					frappe.model.set_value(cdt, cdn, "account_currency", value.account_currency);
+					frm.events.set_exchange_rate(frm, cdt, cdn);
+				});
+			}
+		},
+
+		set_exchange_rate: function(frm, cdt, cdn) {
+			let row = locals[cdt][cdn];
+			let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
+
+			if (row.account_currency == company_currency) {
+				row.exchange_rate = 1;
+				frm.set_df_property('taxes', 'hidden', 1, row.name, 'exchange_rate');
+			} else if (!row.exchange_rate || row.exchange_rate == 1) {
+				frm.set_df_property('taxes', 'hidden', 0, row.name, 'exchange_rate');
+				frappe.call({
+					method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_exchange_rate",
+					args: {
+						posting_date: frm.doc.posting_date,
+						account: row.expense_account,
+						account_currency: row.account_currency,
+						company: frm.doc.company
+					},
+					callback: function(r) {
+						if (r.message) {
+							frappe.model.set_value(cdt, cdn, "exchange_rate", r.message);
+						}
+					}
+				});
+			}
+
+			frm.refresh_field('taxes');
+		},
+
+		set_base_amount: function(frm, cdt, cdn) {
+			let row = locals[cdt][cdn];
+			frappe.model.set_value(cdt, cdn, "base_amount",
+				flt(flt(row.amount)*row.exchange_rate, precision("base_amount", row)));
+		}
+	});
+});
+
diff --git a/erpnext/stock/page/stock_balance/stock_balance.js b/erpnext/stock/page/stock_balance/stock_balance.js
index da21c6b..bddffd4 100644
--- a/erpnext/stock/page/stock_balance/stock_balance.js
+++ b/erpnext/stock/page/stock_balance/stock_balance.js
@@ -65,6 +65,9 @@
 	frappe.require('assets/js/item-dashboard.min.js', function() {
 		page.item_dashboard = new erpnext.stock.ItemDashboard({
 			parent: page.main,
+			page_length: 20,
+			method: 'erpnext.stock.dashboard.item_dashboard.get_data',
+			template: 'item_dashboard_list'
 		})
 
 		page.item_dashboard.before_refresh = function() {
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/stock/page/warehouse_capacity_summary/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/stock/page/warehouse_capacity_summary/__init__.py
diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html
new file mode 100644
index 0000000..90112c7
--- /dev/null
+++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html
@@ -0,0 +1,40 @@
+{% for d in data %}
+	<div class="dashboard-list-item" style="padding: 7px 15px;">
+		<div class="row">
+			<div class="col-sm-2 small" style="margin-top: 8px;">
+				<a data-type="warehouse" data-name="{{ d.warehouse }}">{{ d.warehouse }}</a>
+			</div>
+			<div class="col-sm-2 small" style="margin-top: 8px; ">
+				<a data-type="item" data-name="{{ d.item_code }}">{{ d.item_code }}</a>
+			</div>
+			<div class="col-sm-1 small" style="margin-top: 8px; ">
+				{{ d.stock_capacity }}
+			</div>
+			<div class="col-sm-2 small" style="margin-top: 8px; ">
+				{{ d.actual_qty }}
+			</div>
+			<div class="col-sm-2 small">
+				<div class="progress" title="Occupied Qty: {{ d.actual_qty }}" style="margin-bottom: 4px; height: 7px; margin-top: 14px;">
+					<div class="progress-bar" role="progressbar"
+						aria-valuenow="{{ d.percent_occupied }}"
+						aria-valuemin="0" aria-valuemax="100"
+						style="width:{{ d.percent_occupied }}%;
+						background-color: {{ d.color }}">
+					</div>
+				</div>
+			</div>
+			<div class="col-sm-1 small" style="margin-top: 8px;">
+				{{ d.percent_occupied }}%
+			</div>
+			{% if can_write %}
+			<div class="col-sm-1 text-right" style="margin-top: 2px;">
+				<button class="btn btn-default btn-xs btn-edit"
+				style="margin-top: 4px;margin-bottom: 4px;"
+				data-warehouse="{{ d.warehouse }}"
+				data-item="{{ escape(d.item_code) }}"
+				data-company="{{ escape(d.company) }}">{{ __("Edit Capacity") }}</a>
+			</div>
+			{% endif %}
+		</div>
+	</div>
+{% endfor %}
\ No newline at end of file
diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
new file mode 100644
index 0000000..b610e7d
--- /dev/null
+++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
@@ -0,0 +1,120 @@
+frappe.pages['warehouse-capacity-summary'].on_page_load = function(wrapper) {
+	var page = frappe.ui.make_app_page({
+		parent: wrapper,
+		title: 'Warehouse Capacity Summary',
+		single_column: true
+	});
+	page.set_secondary_action('Refresh', () => page.capacity_dashboard.refresh(), 'octicon octicon-sync');
+	page.start = 0;
+
+	page.company_field = page.add_field({
+		fieldname: 'company',
+		label: __('Company'),
+		fieldtype: 'Link',
+		options: 'Company',
+		reqd: 1,
+		default: frappe.defaults.get_default("company"),
+		change: function() {
+			page.capacity_dashboard.start = 0;
+			page.capacity_dashboard.refresh();
+		}
+	});
+
+	page.warehouse_field = page.add_field({
+		fieldname: 'warehouse',
+		label: __('Warehouse'),
+		fieldtype: 'Link',
+		options: 'Warehouse',
+		change: function() {
+			page.capacity_dashboard.start = 0;
+			page.capacity_dashboard.refresh();
+		}
+	});
+
+	page.item_field = page.add_field({
+		fieldname: 'item_code',
+		label: __('Item'),
+		fieldtype: 'Link',
+		options: 'Item',
+		change: function() {
+			page.capacity_dashboard.start = 0;
+			page.capacity_dashboard.refresh();
+		}
+	});
+
+	page.parent_warehouse_field = page.add_field({
+		fieldname: 'parent_warehouse',
+		label: __('Parent Warehouse'),
+		fieldtype: 'Link',
+		options: 'Warehouse',
+		get_query: function() {
+			return {
+				filters: {
+					"is_group": 1
+				}
+			};
+		},
+		change: function() {
+			page.capacity_dashboard.start = 0;
+			page.capacity_dashboard.refresh();
+		}
+	});
+
+	page.sort_selector = new frappe.ui.SortSelector({
+		parent: page.wrapper.find('.page-form'),
+		args: {
+			sort_by: 'stock_capacity',
+			sort_order: 'desc',
+			options: [
+				{fieldname: 'stock_capacity', label: __('Capacity (Stock UOM)')},
+				{fieldname: 'percent_occupied', label: __('% Occupied')},
+				{fieldname: 'actual_qty', label: __('Balance Qty (Stock ')}
+			]
+		},
+		change: function(sort_by, sort_order) {
+			page.capacity_dashboard.sort_by = sort_by;
+			page.capacity_dashboard.sort_order = sort_order;
+			page.capacity_dashboard.start = 0;
+			page.capacity_dashboard.refresh();
+		}
+	});
+
+	frappe.require('assets/js/item-dashboard.min.js', function() {
+		$(frappe.render_template('warehouse_capacity_summary_header')).appendTo(page.main);
+
+		page.capacity_dashboard = new erpnext.stock.ItemDashboard({
+			page_name: "warehouse-capacity-summary",
+			page_length: 10,
+			parent: page.main,
+			sort_by: 'stock_capacity',
+			sort_order: 'desc',
+			method: 'erpnext.stock.dashboard.warehouse_capacity_dashboard.get_data',
+			template: 'warehouse_capacity_summary'
+		});
+
+		page.capacity_dashboard.before_refresh = function() {
+			this.item_code = page.item_field.get_value();
+			this.warehouse = page.warehouse_field.get_value();
+			this.parent_warehouse = page.parent_warehouse_field.get_value();
+			this.company = page.company_field.get_value();
+		};
+
+		page.capacity_dashboard.refresh();
+
+		let setup_click = function(doctype) {
+			page.main.on('click', 'a[data-type="'+ doctype.toLowerCase() +'"]', function() {
+				var name = $(this).attr('data-name');
+				var field = page[doctype.toLowerCase() + '_field'];
+				if (field.get_value()===name) {
+					frappe.set_route('Form', doctype, name);
+				} else {
+					field.set_input(name);
+					page.capacity_dashboard.refresh();
+				}
+			});
+		};
+
+		setup_click('Item');
+		setup_click('Warehouse');
+	});
+};
\ No newline at end of file
diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.json b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.json
new file mode 100644
index 0000000..a6e5b45
--- /dev/null
+++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.json
@@ -0,0 +1,26 @@
+{
+ "content": null,
+ "creation": "2020-11-25 12:07:54.056208",
+ "docstatus": 0,
+ "doctype": "Page",
+ "idx": 0,
+ "modified": "2020-11-25 11:07:54.056208",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "warehouse-capacity-summary",
+ "owner": "Administrator",
+ "page_name": "Warehouse Capacity Summary",
+ "roles": [
+  {
+   "role": "Stock User"
+  },
+  {
+   "role": "Stock Manager"
+  }
+ ],
+ "script": null,
+ "standard": "Yes",
+ "style": null,
+ "system_page": 0,
+ "title": "Warehouse Capacity Summary"
+}
\ No newline at end of file
diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html
new file mode 100644
index 0000000..acaf180
--- /dev/null
+++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html
@@ -0,0 +1,19 @@
+<div class="dashboard-list-item" style="padding: 12px 15px;">
+	<div class="row">
+		<div class="col-sm-2 small text-muted" style="margin-top: 8px;">
+			Warehouse
+		</div>
+		<div class="col-sm-2 small text-muted" style="margin-top: 8px;">
+			Item
+		</div>
+		<div class="col-sm-1 small text-muted" style="margin-top: 8px;">
+			Stock Capacity
+		</div>
+		<div class="col-sm-2 small text-muted" style="margin-top: 8px;">
+			Balance Stock Qty
+		</div>
+		<div class="col-sm-2 small text-muted" style="margin-top: 8px;">
+			% Occupied
+		</div>
+	</div>
+</div>
\ No newline at end of file
diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py
index 8aaf7ab..ff603fc 100644
--- a/erpnext/stock/report/stock_ageing/stock_ageing.py
+++ b/erpnext/stock/report/stock_ageing/stock_ageing.py
@@ -233,7 +233,8 @@
 				from `tabItem` {item_conditions}) item
 		where item_code = item.name and
 			company = %(company)s and
-			posting_date <= %(to_date)s
+			posting_date <= %(to_date)s and
+			is_cancelled != 1
 			{sle_conditions}
 			order by posting_date, posting_time, sle.creation, actual_qty""" #nosec
 		.format(item_conditions=get_item_conditions(filters),
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 5b9ada0..f54b3c1 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -23,6 +23,7 @@
 
 		cancel = sl_entries[0].get("is_cancelled")
 		if cancel:
+			validate_cancellation(sl_entries)
 			set_as_cancel(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
 
 		for sle in sl_entries:
@@ -41,10 +42,25 @@
 
 			if sle.get("actual_qty") or sle.get("voucher_type")=="Stock Reconciliation":
 				sle_doc = make_entry(sle, allow_negative_stock, via_landed_cost_voucher)
-			
+
 			args = sle_doc.as_dict()
 			update_bin(args, allow_negative_stock, via_landed_cost_voucher)
 
+def validate_cancellation(args):
+	if args[0].get("is_cancelled"):
+		repost_entry = frappe.db.get_value("Repost Item Valuation", {
+			'voucher_type': args[0].voucher_type,
+			'voucher_no': args[0].voucher_no,
+			'docstatus': 1
+		}, ['name', 'status'], as_dict=1)
+
+		if repost_entry:
+			if repost_entry.status == 'In Progress':
+				frappe.throw(_("Cannot cancel the transaction. Reposting of item valuation on submission is not completed yet."))
+			if repost_entry.status == 'Queued':
+				doc = frappe.get_doc("Repost Item Valuation", repost_entry.name)
+				doc.cancel()
+				doc.delete()
 
 def set_as_cancel(voucher_type, voucher_no):
 	frappe.db.sql("""update `tabStock Ledger Entry` set is_cancelled=1,
@@ -62,10 +78,10 @@
 	sle.submit()
 	return sle
 
-def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negative_stock=False, via_landed_cost_voucher=False):
+def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negative_stock=None, via_landed_cost_voucher=False):
 	if not args and voucher_type and voucher_no:
 		args = get_args_for_voucher(voucher_type, voucher_no)
-	
+
 	distinct_item_warehouses = [(d.item_code, d.warehouse) for d in args]
 
 	i = 0
@@ -74,19 +90,20 @@
 			"item_code": args[i].item_code,
 			"warehouse": args[i].warehouse,
 			"posting_date": args[i].posting_date,
-			"posting_time": args[i].posting_time
+			"posting_time": args[i].posting_time,
+			"creation": args[i].get("creation")
 		}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
 
 		for item_wh, new_sle in iteritems(obj.new_items):
 			if item_wh not in distinct_item_warehouses:
 				args.append(new_sle)
-		
+
 		i += 1
 
 def get_args_for_voucher(voucher_type, voucher_no):
 	return frappe.db.get_all("Stock Ledger Entry",
 		filters={"voucher_type": voucher_type, "voucher_no": voucher_no},
-		fields=["item_code", "warehouse", "posting_date", "posting_time"],
+		fields=["item_code", "warehouse", "posting_date", "posting_time", "creation"],
 		order_by="creation asc",
 		group_by="item_code, warehouse"
 	)
@@ -127,7 +144,7 @@
 		self.initialize_previous_data(self.args)
 
 		self.build()
-	
+
 	def get_precision(self):
 		company_base_currency = frappe.get_cached_value('Company',  self.company,  "default_currency")
 		self.precision = get_field_precision(frappe.get_meta("Stock Ledger Entry").get_field("stock_value"),
@@ -155,7 +172,7 @@
 		"""
 		self.data.setdefault(args.warehouse, frappe._dict())
 		warehouse_dict = self.data[args.warehouse]
-		previous_sle = self.get_sle_before_datetime(args)
+		previous_sle = self.get_previous_sle_of_current_voucher(args)
 		warehouse_dict.previous_sle = previous_sle
 
 		for key in ("qty_after_transaction", "valuation_rate", "stock_value"):
@@ -167,9 +184,35 @@
 			"stock_value_difference": 0.0
 		})
 
+	def get_previous_sle_of_current_voucher(self, args):
+		"""get stock ledger entries filtered by specific posting datetime conditions"""
+
+		args['time_format'] = '%H:%i:%s'
+		if not args.get("posting_date"):
+			args["posting_date"] = "1900-01-01"
+		if not args.get("posting_time"):
+			args["posting_time"] = "00:00"
+
+		sle = frappe.db.sql("""
+			select *, timestamp(posting_date, posting_time) as "timestamp"
+			from `tabStock Ledger Entry`
+			where item_code = %(item_code)s
+				and warehouse = %(warehouse)s
+				and is_cancelled = 0
+				and timestamp(posting_date, time_format(posting_time, %(time_format)s)) < timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
+			order by timestamp(posting_date, posting_time) desc, creation desc
+			limit 1""", args, as_dict=1)
+
+		return sle[0] if sle else frappe._dict()
+
+
 	def build(self):
+		from erpnext.controllers.stock_controller import check_if_future_sle_exists
+
 		if self.args.get("sle_id"):
-			self.process_sle_against_current_voucher()
+			self.process_sle_against_current_timestamp()
+			if not check_if_future_sle_exists(self.args):
+				self.update_bin()
 		else:
 			entries_to_fix = self.get_future_entries_to_fix()
 
@@ -181,19 +224,21 @@
 				self.process_sle(sle)
 
 				if sle.dependant_sle_voucher_detail_no:
-					self.get_dependent_entries_to_fix(entries_to_fix, sle)
+					entries_to_fix = self.get_dependent_entries_to_fix(entries_to_fix, sle)
+
+			self.update_bin()
 
 		if self.exceptions:
 			self.raise_exceptions()
 
-		self.update_bin()
-
-	def process_sle_against_current_voucher(self):
+	def process_sle_against_current_timestamp(self):
 		sl_entries = self.get_sle_against_current_voucher()
 		for sle in sl_entries:
 			self.process_sle(sle)
 
 	def get_sle_against_current_voucher(self):
+		self.args['time_format'] = '%H:%i:%s'
+
 		return frappe.db.sql("""
 			select
 				*, timestamp(posting_date, posting_time) as "timestamp"
@@ -202,8 +247,8 @@
 			where
 				item_code = %(item_code)s
 				and warehouse = %(warehouse)s
-				and voucher_type = %(voucher_type)s
-				and voucher_no = %(voucher_no)s
+				and timestamp(posting_date, time_format(posting_time, %(time_format)s)) = timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
+
 			order by
 				creation ASC
 			for update
@@ -213,22 +258,23 @@
 		# includes current entry!
 		args = self.data[self.args.warehouse].previous_sle \
 			or frappe._dict({"item_code": self.item_code, "warehouse": self.args.warehouse})
-		
+
 		return list(self.get_sle_after_datetime(args))
 
 	def get_dependent_entries_to_fix(self, entries_to_fix, sle):
 		dependant_sle = get_sle_by_voucher_detail_no(sle.dependant_sle_voucher_detail_no,
 			excluded_sle=sle.name)
-		
-		if not dependant_sle:
-			return
-		elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse == self.args.warehouse:
-			return
-		elif dependant_sle.item_code != self.item_code \
-				and (dependant_sle.item_code, dependant_sle.warehouse) not in self.new_items:
-			self.new_items[(dependant_sle.item_code, dependant_sle.warehouse)] = dependant_sle
-			return
 
+		if not dependant_sle:
+			return entries_to_fix
+		elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse == self.args.warehouse:
+			return entries_to_fix
+		elif dependant_sle.item_code != self.item_code:
+			if (dependant_sle.item_code, dependant_sle.warehouse) not in self.new_items:
+				self.new_items[(dependant_sle.item_code, dependant_sle.warehouse)] = dependant_sle
+			return entries_to_fix
+		elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse in self.data:
+			return entries_to_fix
 		self.initialize_previous_data(dependant_sle)
 
 		args = self.data[dependant_sle.warehouse].previous_sle \
@@ -236,7 +282,7 @@
 		future_sle_for_dependant = list(self.get_sle_after_datetime(args))
 
 		entries_to_fix.extend(future_sle_for_dependant)
-		entries_to_fix = sorted(entries_to_fix, key=lambda k: k['timestamp'])
+		return sorted(entries_to_fix, key=lambda k: k['timestamp'])
 
 	def process_sle(self, sle):
 		# previous sle data for this warehouse
@@ -251,7 +297,7 @@
 
 		# Get dynamic incoming/outgoing rate
 		self.get_dynamic_incoming_outgoing_rate(sle)
-		
+
 		if sle.serial_no:
 			self.get_serialized_values(sle)
 			self.wh_data.qty_after_transaction += flt(sle.actual_qty)
@@ -329,7 +375,7 @@
 				rate = get_rate_for_return(sle.voucher_type, sle.voucher_no, sle.item_code, voucher_detail_no=sle.voucher_detail_no)
 			else:
 				if sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"):
-					rate_field = "valuation_rate" 
+					rate_field = "valuation_rate"
 				else:
 					rate_field = "incoming_rate"
 
@@ -344,7 +390,7 @@
 						ref_doctype = "Packed Item"
 					else:
 						ref_doctype = "Purchase Receipt Item Supplied"
-	
+
 					rate = frappe.db.get_value(ref_doctype, {"parent_detail_docname": sle.voucher_detail_no,
 						"item_code": sle.item_code}, rate_field)
 
@@ -374,7 +420,7 @@
 		stock_entry.db_update()
 		for d in stock_entry.items:
 			d.db_update()
-	
+
 	def update_rate_on_delivery_and_sales_return(self, sle, outgoing_rate):
 		# Update item's incoming rate on transaction
 		item_code = frappe.db.get_value(sle.voucher_type + " Item", sle.voucher_detail_no, "item_code")
@@ -394,7 +440,7 @@
 
 		# Recalculate subcontracted item's rate in case of subcontracted purchase receipt/invoice
 		if frappe.db.get_value(sle.voucher_type, sle.voucher_no, "is_subcontracted"):
-			doc = frappe.get_cached_doc(sle.voucher_type, sle.voucher_no)
+			doc = frappe.get_doc(sle.voucher_type, sle.voucher_no)
 			doc.update_valuation_rate(reset_outgoing_rate=False)
 			for d in (doc.items + doc.supplied_items):
 				d.db_update()
@@ -487,7 +533,6 @@
 					self.wh_data.valuation_rate = new_stock_value / new_stock_qty
 				else:
 					self.wh_data.valuation_rate = sle.outgoing_rate
-
 		else:
 			if flt(self.wh_data.qty_after_transaction) >= 0 and sle.outgoing_rate:
 				self.wh_data.valuation_rate = sle.outgoing_rate
@@ -613,11 +658,11 @@
 				frappe.local.flags.currently_saving):
 
 				msg = _("{0} units of {1} needed in {2} to complete this transaction.").format(
-					abs(deficiency), frappe.get_desk_link('Item', self.item_code),
+					abs(deficiency), frappe.get_desk_link('Item', exceptions[0]["item_code"]),
 					frappe.get_desk_link('Warehouse', warehouse))
 			else:
 				msg = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format(
-					abs(deficiency), frappe.get_desk_link('Item', self.item_code),
+					abs(deficiency), frappe.get_desk_link('Item', exceptions[0]["item_code"]),
 					frappe.get_desk_link('Warehouse', warehouse),
 					exceptions[0]["posting_date"], exceptions[0]["posting_time"],
 					frappe.get_desk_link(exceptions[0]["voucher_type"], exceptions[0]["voucher_no"]))
@@ -631,12 +676,11 @@
 				frappe.throw(message, NegativeStockError, title='Insufficient Stock')
 			else:
 				raise NegativeStockError(message)
-	
+
 	def update_bin(self):
 		# update bin for each warehouse
 		for warehouse, data in iteritems(self.data):
 			bin_doc = get_bin(self.item_code, warehouse)
-
 			bin_doc.update({
 				"valuation_rate": data.valuation_rate,
 				"actual_qty": data.qty_after_transaction,
@@ -766,7 +810,7 @@
 	frappe.db.sql("""
 		update `tabStock Ledger Entry`
 		set qty_after_transaction = qty_after_transaction + {qty}
-		where 
+		where
 			item_code = %(item_code)s
 			and warehouse = %(warehouse)s
 			and voucher_no != %(voucher_no)s
@@ -794,7 +838,7 @@
 				frappe.get_desk_link('Warehouse', args.warehouse),
 				sle[0]["posting_date"], sle[0]["posting_time"],
 				frappe.get_desk_link(sle[0]["voucher_type"], sle[0]["voucher_no"]))
-						
+
 			frappe.throw(message, NegativeStockError, title='Insufficient Stock')
 
 def get_future_sle_with_negative_qty(args):
@@ -803,12 +847,13 @@
 			qty_after_transaction, posting_date, posting_time,
 			voucher_type, voucher_no
 		from `tabStock Ledger Entry`
-		where 
+		where
 			item_code = %(item_code)s
 			and warehouse = %(warehouse)s
 			and voucher_no != %(voucher_no)s
 			and timestamp(posting_date, posting_time) >= timestamp(%(posting_date)s, %(posting_time)s)
 			and is_cancelled = 0
 			and qty_after_transaction < 0
+		order by timestamp(posting_date, posting_time) asc
 		limit 1
 	""", args, as_dict=1)
\ No newline at end of file
diff --git a/erpnext/support/desk_page/support/support.json b/erpnext/support/desk_page/support/support.json
index 28410f3..dba2b14 100644
--- a/erpnext/support/desk_page/support/support.json
+++ b/erpnext/support/desk_page/support/support.json
@@ -28,7 +28,7 @@
   {
    "hidden": 0,
    "label": "Reports",
-   "links": "[\n    {\n        \"dependencies\": [\n            \"Issue\"\n        ],\n        \"doctype\": \"Issue\",\n        \"is_query_report\": true,\n        \"label\": \"First Response Time for Issues\",\n        \"name\": \"First Response Time for Issues\",\n        \"type\": \"report\"\n    }\n]"
+   "links": "[\n    {\n        \"dependencies\": [\n            \"Issue\"\n        ],\n        \"doctype\": \"Issue\",\n        \"is_query_report\": true,\n        \"label\": \"First Response Time for Issues\",\n        \"name\": \"First Response Time for Issues\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Issue\"\n        ],\n        \"doctype\": \"Issue\",\n        \"is_query_report\": true,\n        \"label\": \"Issue Analytics\",\n        \"name\": \"Issue Analytics\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Issue\"\n        ],\n        \"doctype\": \"Issue\",\n        \"is_query_report\": true,\n        \"label\": \"Issue Summary\",\n        \"name\": \"Issue Summary\",\n        \"type\": \"report\"\n    }\n]"
   }
  ],
  "category": "Modules",
@@ -43,7 +43,7 @@
  "idx": 0,
  "is_standard": 1,
  "label": "Support",
- "modified": "2020-08-11 15:49:34.307341",
+ "modified": "2021-01-13 20:15:03.064256",
  "modified_by": "Administrator",
  "module": "Support",
  "name": "Support",
diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py
index 62b39cc..1ac2959 100644
--- a/erpnext/support/doctype/issue/issue.py
+++ b/erpnext/support/doctype/issue/issue.py
@@ -214,7 +214,10 @@
 
 	def before_insert(self):
 		if frappe.db.get_single_value("Support Settings", "track_service_level_agreement"):
-			self.set_response_and_resolution_time()
+			if frappe.flags.in_test:
+				self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement)
+			else:
+				self.set_response_and_resolution_time()
 
 	def set_response_and_resolution_time(self, priority=None, service_level_agreement=None):
 		service_level_agreement = get_active_service_level_agreement_for(priority=priority,
diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py
index c962dc6..483bb15 100644
--- a/erpnext/support/doctype/issue/test_issue.py
+++ b/erpnext/support/doctype/issue/test_issue.py
@@ -135,15 +135,19 @@
 		self.assertEqual(flt(issue.total_hold_time, 2), 2700)
 
 
-def make_issue(creation=None, customer=None, index=0):
+def make_issue(creation=None, customer=None, index=0, priority=None, issue_type=None):
 	issue = frappe.get_doc({
 		"doctype": "Issue",
 		"subject": "Service Level Agreement Issue {0}".format(index),
 		"customer": customer,
 		"raised_by": "test@example.com",
 		"description": "Service Level Agreement Issue",
+		"issue_type": issue_type,
+		"priority": priority,
 		"creation": creation,
-		"service_level_agreement_creation": creation
+		"opening_date": creation,
+		"service_level_agreement_creation": creation,
+		"company": "_Test Company"
 	}).insert(ignore_permissions=True)
 
 	return issue
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/support/report/issue_analytics/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/support/report/issue_analytics/__init__.py
diff --git a/erpnext/support/report/issue_analytics/issue_analytics.js b/erpnext/support/report/issue_analytics/issue_analytics.js
new file mode 100644
index 0000000..f87b2c2
--- /dev/null
+++ b/erpnext/support/report/issue_analytics/issue_analytics.js
@@ -0,0 +1,141 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Issue Analytics"] = {
+	"filters": [
+		{
+			fieldname: "company",
+			label: __("Company"),
+			fieldtype: "Link",
+			options: "Company",
+			default: frappe.defaults.get_user_default("Company"),
+			reqd: 1
+		},
+		{
+			fieldname: "based_on",
+			label: __("Based On"),
+			fieldtype: "Select",
+			options: ["Customer", "Issue Type", "Issue Priority", "Assigned To"],
+			default: "Customer",
+			reqd: 1
+		},
+		{
+			fieldname: "from_date",
+			label: __("From Date"),
+			fieldtype: "Date",
+			default: frappe.defaults.get_global_default("year_start_date"),
+			reqd: 1
+		},
+		{
+			fieldname:"to_date",
+			label: __("To Date"),
+			fieldtype: "Date",
+			default: frappe.defaults.get_global_default("year_end_date"),
+			reqd: 1
+		},
+		{
+			fieldname: "range",
+			label: __("Range"),
+			fieldtype: "Select",
+			options: [
+				{ "value": "Weekly", "label": __("Weekly") },
+				{ "value": "Monthly", "label": __("Monthly") },
+				{ "value": "Quarterly", "label": __("Quarterly") },
+				{ "value": "Yearly", "label": __("Yearly") }
+			],
+			default: "Monthly",
+			reqd: 1
+		},
+		{
+			fieldname: "status",
+			label: __("Status"),
+			fieldtype: "Select",
+			options:[
+				{label: __('Open'), value: 'Open'},
+				{label: __('Replied'), value: 'Replied'},
+				{label: __('Resolved'), value: 'Resolved'},
+				{label: __('Closed'), value: 'Closed'}
+			]
+		},
+		{
+			fieldname: "priority",
+			label: __("Issue Priority"),
+			fieldtype: "Link",
+			options: "Issue Priority"
+		},
+		{
+			fieldname: "customer",
+			label: __("Customer"),
+			fieldtype: "Link",
+			options: "Customer"
+		},
+		{
+			fieldname: "project",
+			label: __("Project"),
+			fieldtype: "Link",
+			options: "Project"
+		},
+		{
+			fieldname: "assigned_to",
+			label: __("Assigned To"),
+			fieldtype: "Link",
+			options: "User"
+		}
+	],
+	after_datatable_render: function(datatable_obj) {
+		$(datatable_obj.wrapper).find(".dt-row-0").find('input[type=checkbox]').click();
+	},
+	get_datatable_options(options) {
+		return Object.assign(options, {
+			checkboxColumn: true,
+			events: {
+				onCheckRow: function(data) {
+					if (data && data.length) {
+						row_name = data[2].content;
+						row_values = data.slice(3).map(function(column) {
+							return column.content;
+						})
+						entry  = {
+							'name': row_name,
+							'values': row_values
+						}
+
+						let raw_data = frappe.query_report.chart.data;
+						let new_datasets = raw_data.datasets;
+
+						var found = false;
+
+						for(var i=0; i < new_datasets.length; i++){
+							if (new_datasets[i].name == row_name){
+								found = true;
+								new_datasets.splice(i,1);
+								break;
+							}
+						}
+
+						if (!found){
+							new_datasets.push(entry);
+						}
+
+						let new_data = {
+							labels: raw_data.labels,
+							datasets: new_datasets
+						}
+
+						setTimeout(() => {
+							frappe.query_report.chart.update(new_data)
+						},500)
+
+
+						setTimeout(() => {
+							frappe.query_report.chart.draw(true);
+						}, 1000)
+
+						frappe.query_report.raw_chart_data = new_data;
+					}
+				},
+			}
+		});
+	}
+};
\ No newline at end of file
diff --git a/erpnext/support/report/issue_analytics/issue_analytics.json b/erpnext/support/report/issue_analytics/issue_analytics.json
new file mode 100644
index 0000000..dd18498
--- /dev/null
+++ b/erpnext/support/report/issue_analytics/issue_analytics.json
@@ -0,0 +1,26 @@
+{
+ "add_total_row": 1,
+ "columns": [],
+ "creation": "2020-10-09 19:52:10.227317",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-10-11 19:43:19.358625",
+ "modified_by": "Administrator",
+ "module": "Support",
+ "name": "Issue Analytics",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Issue",
+ "report_name": "Issue Analytics",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Support Team"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/support/report/issue_analytics/issue_analytics.py b/erpnext/support/report/issue_analytics/issue_analytics.py
new file mode 100644
index 0000000..3fdb10d
--- /dev/null
+++ b/erpnext/support/report/issue_analytics/issue_analytics.py
@@ -0,0 +1,221 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+import json
+from six import iteritems
+from frappe import _, scrub
+from frappe.utils import getdate, flt, add_to_date, add_days
+from erpnext.accounts.utils import get_fiscal_year
+
+def execute(filters=None):
+	return IssueAnalytics(filters).run()
+
+class IssueAnalytics(object):
+	def __init__(self, filters=None):
+		"""Issue Analytics Report"""
+		self.filters = frappe._dict(filters or {})
+		self.get_period_date_ranges()
+
+	def run(self):
+		self.get_columns()
+		self.get_data()
+		self.get_chart_data()
+
+		return self.columns, self.data, None, self.chart
+
+	def get_columns(self):
+		self.columns = []
+
+		if self.filters.based_on == 'Customer':
+			self.columns.append({
+				'label': _('Customer'),
+				'options': 'Customer',
+				'fieldname': 'customer',
+				'fieldtype': 'Link',
+				'width': 200
+			})
+
+		elif self.filters.based_on == 'Assigned To':
+			self.columns.append({
+				'label': _('User'),
+				'fieldname': 'user',
+				'fieldtype': 'Link',
+				'options': 'User',
+				'width': 200
+			})
+
+		elif self.filters.based_on == 'Issue Type':
+			self.columns.append({
+				'label': _('Issue Type'),
+				'fieldname': 'issue_type',
+				'fieldtype': 'Link',
+				'options': 'Issue Type',
+				'width': 200
+			})
+
+		elif self.filters.based_on == 'Issue Priority':
+			self.columns.append({
+				'label': _('Issue Priority'),
+				'fieldname': 'priority',
+				'fieldtype': 'Link',
+				'options': 'Issue Priority',
+				'width': 200
+			})
+
+		for end_date in self.periodic_daterange:
+			period = self.get_period(end_date)
+			self.columns.append({
+				'label': _(period),
+				'fieldname': scrub(period),
+				'fieldtype': 'Int',
+				'width': 120
+			})
+
+		self.columns.append({
+			'label': _('Total'),
+			'fieldname': 'total',
+			'fieldtype': 'Int',
+			'width': 120
+		})
+
+	def get_data(self):
+		self.get_issues()
+		self.get_rows()
+
+	def get_period(self, date):
+		months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+
+		if self.filters.range == 'Weekly':
+			period = 'Week ' + str(date.isocalendar()[1])
+		elif self.filters.range == 'Monthly':
+			period = str(months[date.month - 1])
+		elif self.filters.range == 'Quarterly':
+			period = 'Quarter ' + str(((date.month - 1) // 3) + 1)
+		else:
+			year = get_fiscal_year(date, self.filters.company)
+			period = str(year[0])
+
+		if getdate(self.filters.from_date).year != getdate(self.filters.to_date).year and self.filters.range != 'Yearly':
+			period += ' ' + str(date.year)
+
+		return period
+
+	def get_period_date_ranges(self):
+		from dateutil.relativedelta import relativedelta, MO
+		from_date, to_date = getdate(self.filters.from_date), getdate(self.filters.to_date)
+
+		increment = {
+			'Monthly': 1,
+			'Quarterly': 3,
+			'Half-Yearly': 6,
+			'Yearly': 12
+		}.get(self.filters.range, 1)
+
+		if self.filters.range in ['Monthly', 'Quarterly']:
+			from_date = from_date.replace(day=1)
+		elif self.filters.range == 'Yearly':
+			from_date = get_fiscal_year(from_date)[1]
+		else:
+			from_date = from_date + relativedelta(from_date, weekday=MO(-1))
+
+		self.periodic_daterange = []
+		for dummy in range(1, 53):
+			if self.filters.range == 'Weekly':
+				period_end_date = add_days(from_date, 6)
+			else:
+				period_end_date = add_to_date(from_date, months=increment, days=-1)
+
+			if period_end_date > to_date:
+				period_end_date = to_date
+
+			self.periodic_daterange.append(period_end_date)
+
+			from_date = add_days(period_end_date, 1)
+			if period_end_date == to_date:
+				break
+
+	def get_issues(self):
+		filters = self.get_common_filters()
+		self.field_map = {
+			'Customer': 'customer',
+			'Issue Type': 'issue_type',
+			'Issue Priority': 'priority',
+			'Assigned To': '_assign'
+		}
+
+		self.entries = frappe.db.get_all('Issue',
+			fields=[self.field_map.get(self.filters.based_on), 'name', 'opening_date'],
+			filters=filters
+		)
+
+	def get_common_filters(self):
+		filters = {}
+		filters['opening_date'] = ('between', [self.filters.from_date, self.filters.to_date])
+
+		if self.filters.get('assigned_to'):
+			filters['_assign'] = ('like', '%' + self.filters.get('assigned_to') + '%')
+
+		for entry in ['company', 'status', 'priority', 'customer', 'project']:
+			if self.filters.get(entry):
+				filters[entry] = self.filters.get(entry)
+
+		return filters
+
+	def get_rows(self):
+		self.data = []
+		self.get_periodic_data()
+
+		for entity, period_data in iteritems(self.issue_periodic_data):
+			if self.filters.based_on == 'Customer':
+				row = {'customer': entity}
+			elif self.filters.based_on == 'Assigned To':
+				row = {'user': entity}
+			elif self.filters.based_on == 'Issue Type':
+				row = {'issue_type': entity}
+			elif self.filters.based_on == 'Issue Priority':
+				row = {'priority': entity}
+
+			total = 0
+			for end_date in self.periodic_daterange:
+				period = self.get_period(end_date)
+				amount = flt(period_data.get(period, 0.0))
+				row[scrub(period)] = amount
+				total += amount
+
+			row['total'] = total
+
+			self.data.append(row)
+
+	def get_periodic_data(self):
+		self.issue_periodic_data = frappe._dict()
+
+		for d in self.entries:
+			period = self.get_period(d.get('opening_date'))
+
+			if self.filters.based_on == 'Assigned To':
+				if d._assign:
+					for entry in json.loads(d._assign):
+						self.issue_periodic_data.setdefault(entry, frappe._dict()).setdefault(period, 0.0)
+						self.issue_periodic_data[entry][period] += 1
+
+			else:
+				field = self.field_map.get(self.filters.based_on)
+				value = d.get(field)
+				if not value:
+					value = _('Not Specified')
+
+				self.issue_periodic_data.setdefault(value, frappe._dict()).setdefault(period, 0.0)
+				self.issue_periodic_data[value][period] += 1
+
+	def get_chart_data(self):
+		length = len(self.columns)
+		labels = [d.get('label') for d in self.columns[1:length-1]]
+		self.chart = {
+			'data': {
+				'labels': labels,
+				'datasets': []
+			},
+			'type': 'line'
+		}
\ No newline at end of file
diff --git a/erpnext/support/report/issue_analytics/test_issue_analytics.py b/erpnext/support/report/issue_analytics/test_issue_analytics.py
new file mode 100644
index 0000000..fc6bb58
--- /dev/null
+++ b/erpnext/support/report/issue_analytics/test_issue_analytics.py
@@ -0,0 +1,214 @@
+from __future__ import unicode_literals
+import unittest
+import frappe
+from frappe.utils import getdate, add_months
+from erpnext.support.report.issue_analytics.issue_analytics import execute
+from erpnext.support.doctype.issue.test_issue import make_issue, create_customer
+from erpnext.support.doctype.service_level_agreement.test_service_level_agreement import create_service_level_agreements_for_issues
+from frappe.desk.form.assign_to import add as add_assignment
+
+months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+
+class TestIssueAnalytics(unittest.TestCase):
+	@classmethod
+	def setUpClass(self):
+		frappe.db.sql("delete from `tabIssue` where company='_Test Company'")
+		frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1)
+
+		current_month_date = getdate()
+		last_month_date = add_months(current_month_date, -1)
+		self.current_month = str(months[current_month_date.month - 1]).lower()
+		self.last_month = str(months[last_month_date.month - 1]).lower()
+		if current_month_date.year != last_month_date.year:
+			self.current_month += '_' + str(current_month_date.year)
+			self.last_month += '_' + str(last_month_date.year)
+
+	def test_issue_analytics(self):
+		create_service_level_agreements_for_issues()
+		create_issue_types()
+		create_records()
+
+		self.compare_result_for_customer()
+		self.compare_result_for_issue_type()
+		self.compare_result_for_issue_priority()
+		self.compare_result_for_assignment()
+
+	def compare_result_for_customer(self):
+		filters = {
+			'company': '_Test Company',
+			'based_on': 'Customer',
+			'from_date': add_months(getdate(), -1),
+			'to_date': getdate(),
+			'range': 'Monthly'
+		}
+
+		report = execute(filters)
+
+		expected_data = [
+			{
+				'customer': '__Test Customer 2',
+				self.last_month: 1.0,
+				self.current_month: 0.0,
+				'total': 1.0
+			},
+			{
+				'customer': '__Test Customer 1',
+				self.last_month: 0.0,
+				self.current_month: 1.0,
+				'total': 1.0
+			},
+			{
+				'customer': '__Test Customer',
+				self.last_month: 1.0,
+				self.current_month: 1.0,
+				'total': 2.0
+			}
+		]
+
+		self.assertEqual(expected_data, report[1]) # rows
+		self.assertEqual(len(report[0]), 4) # cols
+
+	def compare_result_for_issue_type(self):
+		filters = {
+			'company': '_Test Company',
+			'based_on': 'Issue Type',
+			'from_date': add_months(getdate(), -1),
+			'to_date': getdate(),
+			'range': 'Monthly'
+		}
+
+		report = execute(filters)
+
+		expected_data = [
+			{
+				'issue_type': 'Discomfort',
+				self.last_month: 1.0,
+				self.current_month: 0.0,
+				'total': 1.0
+			},
+			{
+				'issue_type': 'Service Request',
+				self.last_month: 0.0,
+				self.current_month: 1.0,
+				'total': 1.0
+			},
+			{
+				'issue_type': 'Bug',
+				self.last_month: 1.0,
+				self.current_month: 1.0,
+				'total': 2.0
+			}
+		]
+
+		self.assertEqual(expected_data, report[1]) # rows
+		self.assertEqual(len(report[0]), 4) # cols
+
+	def compare_result_for_issue_priority(self):
+		filters = {
+			'company': '_Test Company',
+			'based_on': 'Issue Priority',
+			'from_date': add_months(getdate(), -1),
+			'to_date': getdate(),
+			'range': 'Monthly'
+		}
+
+		report = execute(filters)
+
+		expected_data = [
+			{
+				'priority': 'Medium',
+				self.last_month: 1.0,
+				self.current_month: 1.0,
+				'total': 2.0
+			},
+			{
+				'priority': 'Low',
+				self.last_month: 1.0,
+				self.current_month: 0.0,
+				'total': 1.0
+			},
+			{
+				'priority': 'High',
+				self.last_month: 0.0,
+				self.current_month: 1.0,
+				'total': 1.0
+			}
+		]
+
+		self.assertEqual(expected_data, report[1]) # rows
+		self.assertEqual(len(report[0]), 4) # cols
+
+	def compare_result_for_assignment(self):
+		filters = {
+			'company': '_Test Company',
+			'based_on': 'Assigned To',
+			'from_date': add_months(getdate(), -1),
+			'to_date': getdate(),
+			'range': 'Monthly'
+		}
+
+		report = execute(filters)
+
+		expected_data = [
+			{
+				'user': 'test@example.com',
+				self.last_month: 1.0,
+				self.current_month: 1.0,
+				'total': 2.0
+			},
+			{
+				'user': 'test1@example.com',
+				self.last_month: 2.0,
+				self.current_month: 1.0,
+				'total': 3.0
+			}
+		]
+
+		self.assertEqual(expected_data, report[1]) # rows
+		self.assertEqual(len(report[0]), 4) # cols
+
+
+def create_issue_types():
+	for entry in ['Bug', 'Service Request', 'Discomfort']:
+		if not frappe.db.exists('Issue Type', entry):
+			frappe.get_doc({
+				'doctype': 'Issue Type',
+				'__newname': entry
+			}).insert()
+
+
+def create_records():
+	create_customer("__Test Customer", "_Test SLA Customer Group", "__Test SLA Territory")
+	create_customer("__Test Customer 1", "_Test SLA Customer Group", "__Test SLA Territory")
+	create_customer("__Test Customer 2", "_Test SLA Customer Group", "__Test SLA Territory")
+
+	current_month_date = getdate()
+	last_month_date = add_months(current_month_date, -1)
+
+	issue = make_issue(current_month_date, "__Test Customer", 2, "High", "Bug")
+	add_assignment({
+		"assign_to": ["test@example.com"],
+		"doctype": "Issue",
+		"name": issue.name
+	})
+
+	issue = make_issue(last_month_date, "__Test Customer", 2, "Low", "Bug")
+	add_assignment({
+		"assign_to": ["test1@example.com"],
+		"doctype": "Issue",
+		"name": issue.name
+	})
+
+	issue = make_issue(current_month_date, "__Test Customer 1", 2, "Medium", "Service Request")
+	add_assignment({
+		"assign_to": ["test1@example.com"],
+		"doctype": "Issue",
+		"name": issue.name
+	})
+
+	issue = make_issue(last_month_date, "__Test Customer 2", 2, "Medium", "Discomfort")
+	add_assignment({
+		"assign_to": ["test@example.com", "test1@example.com"],
+		"doctype": "Issue",
+		"name": issue.name
+	})
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/support/report/issue_summary/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/support/report/issue_summary/__init__.py
diff --git a/erpnext/support/report/issue_summary/issue_summary.js b/erpnext/support/report/issue_summary/issue_summary.js
new file mode 100644
index 0000000..684482a
--- /dev/null
+++ b/erpnext/support/report/issue_summary/issue_summary.js
@@ -0,0 +1,73 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Issue Summary"] = {
+	"filters": [
+		{
+			fieldname: "company",
+			label: __("Company"),
+			fieldtype: "Link",
+			options: "Company",
+			default: frappe.defaults.get_user_default("Company"),
+			reqd: 1
+		},
+		{
+			fieldname: "based_on",
+			label: __("Based On"),
+			fieldtype: "Select",
+			options: ["Customer", "Issue Type", "Issue Priority", "Assigned To"],
+			default: "Customer",
+			reqd: 1
+		},
+		{
+			fieldname: "from_date",
+			label: __("From Date"),
+			fieldtype: "Date",
+			default: frappe.defaults.get_global_default("year_start_date"),
+			reqd: 1
+		},
+		{
+			fieldname:"to_date",
+			label: __("To Date"),
+			fieldtype: "Date",
+			default: frappe.defaults.get_global_default("year_end_date"),
+			reqd: 1
+		},
+		{
+			fieldname: "status",
+			label: __("Status"),
+			fieldtype: "Select",
+			options:[
+				{label: __('Open'), value: 'Open'},
+				{label: __('Replied'), value: 'Replied'},
+				{label: __('Resolved'), value: 'Resolved'},
+				{label: __('Closed'), value: 'Closed'}
+			]
+		},
+		{
+			fieldname: "priority",
+			label: __("Issue Priority"),
+			fieldtype: "Link",
+			options: "Issue Priority"
+		},
+		{
+			fieldname: "customer",
+			label: __("Customer"),
+			fieldtype: "Link",
+			options: "Customer"
+		},
+		{
+			fieldname: "project",
+			label: __("Project"),
+			fieldtype: "Link",
+			options: "Project"
+		},
+		{
+			fieldname: "assigned_to",
+			label: __("Assigned To"),
+			fieldtype: "Link",
+			options: "User"
+		}
+	]
+};
\ No newline at end of file
diff --git a/erpnext/support/report/issue_summary/issue_summary.json b/erpnext/support/report/issue_summary/issue_summary.json
new file mode 100644
index 0000000..b8a580c
--- /dev/null
+++ b/erpnext/support/report/issue_summary/issue_summary.json
@@ -0,0 +1,26 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2020-10-12 01:01:55.181777",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-10-12 14:54:55.655920",
+ "modified_by": "Administrator",
+ "module": "Support",
+ "name": "Issue Summary",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Issue",
+ "report_name": "Issue Summary",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Support Team"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/support/report/issue_summary/issue_summary.py b/erpnext/support/report/issue_summary/issue_summary.py
new file mode 100644
index 0000000..3d73531
--- /dev/null
+++ b/erpnext/support/report/issue_summary/issue_summary.py
@@ -0,0 +1,353 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+import json
+from six import iteritems
+from frappe import _, scrub
+from frappe.utils import flt
+
+def execute(filters=None):
+	return IssueSummary(filters).run()
+
+class IssueSummary(object):
+	def __init__(self, filters=None):
+		self.filters = frappe._dict(filters or {})
+
+	def run(self):
+		self.get_columns()
+		self.get_data()
+		self.get_chart_data()
+		self.get_report_summary()
+
+		return self.columns, self.data, None, self.chart, self.report_summary
+
+	def get_columns(self):
+		self.columns = []
+
+		if self.filters.based_on == 'Customer':
+			self.columns.append({
+				'label': _('Customer'),
+				'options': 'Customer',
+				'fieldname': 'customer',
+				'fieldtype': 'Link',
+				'width': 200
+			})
+
+		elif self.filters.based_on == 'Assigned To':
+			self.columns.append({
+				'label': _('User'),
+				'fieldname': 'user',
+				'fieldtype': 'Link',
+				'options': 'User',
+				'width': 200
+			})
+
+		elif self.filters.based_on == 'Issue Type':
+			self.columns.append({
+				'label': _('Issue Type'),
+				'fieldname': 'issue_type',
+				'fieldtype': 'Link',
+				'options': 'Issue Type',
+				'width': 200
+			})
+
+		elif self.filters.based_on == 'Issue Priority':
+			self.columns.append({
+				'label': _('Issue Priority'),
+				'fieldname': 'priority',
+				'fieldtype': 'Link',
+				'options': 'Issue Priority',
+				'width': 200
+			})
+
+		self.statuses = ['Open', 'Replied', 'Resolved', 'Closed']
+		for status in self.statuses:
+			self.columns.append({
+				'label': _(status),
+				'fieldname': scrub(status),
+				'fieldtype': 'Int',
+				'width': 80
+			})
+
+		self.columns.append({
+			'label': _('Total Issues'),
+			'fieldname': 'total_issues',
+			'fieldtype': 'Int',
+			'width': 100
+		})
+
+		self.sla_status_map = {
+			'SLA Failed': 'failed',
+			'SLA Fulfilled': 'fulfilled',
+			'SLA Ongoing': 'ongoing'
+		}
+
+		for label, fieldname in self.sla_status_map.items():
+			self.columns.append({
+				'label': _(label),
+				'fieldname': fieldname,
+				'fieldtype': 'Int',
+				'width': 100
+			})
+
+		self.metrics = ['Avg First Response Time', 'Avg Response Time', 'Avg Hold Time',
+			'Avg Resolution Time', 'Avg User Resolution Time']
+
+		for metric in self.metrics:
+			self.columns.append({
+				'label': _(metric),
+				'fieldname': scrub(metric),
+				'fieldtype': 'Duration',
+				'width': 170
+			})
+
+	def get_data(self):
+		self.get_issues()
+		self.get_rows()
+
+	def get_issues(self):
+		filters = self.get_common_filters()
+		self.field_map = {
+			'Customer': 'customer',
+			'Issue Type': 'issue_type',
+			'Issue Priority': 'priority',
+			'Assigned To': '_assign'
+		}
+
+		self.entries = frappe.db.get_all('Issue',
+			fields=[self.field_map.get(self.filters.based_on), 'name', 'opening_date', 'status', 'avg_response_time',
+				'first_response_time', 'total_hold_time', 'user_resolution_time', 'resolution_time', 'agreement_status'],
+			filters=filters
+		)
+
+	def get_common_filters(self):
+		filters = {}
+		filters['opening_date'] = ('between', [self.filters.from_date, self.filters.to_date])
+
+		if self.filters.get('assigned_to'):
+			filters['_assign'] = ('like', '%' + self.filters.get('assigned_to') + '%')
+
+		for entry in ['company', 'status', 'priority', 'customer', 'project']:
+			if self.filters.get(entry):
+				filters[entry] = self.filters.get(entry)
+
+		return filters
+
+	def get_rows(self):
+		self.data = []
+		self.get_summary_data()
+
+		for entity, data in iteritems(self.issue_summary_data):
+			if self.filters.based_on == 'Customer':
+				row = {'customer': entity}
+			elif self.filters.based_on == 'Assigned To':
+				row = {'user': entity}
+			elif self.filters.based_on == 'Issue Type':
+				row = {'issue_type': entity}
+			elif self.filters.based_on == 'Issue Priority':
+				row = {'priority': entity}
+
+			for status in self.statuses:
+				count = flt(data.get(status, 0.0))
+				row[scrub(status)] = count
+
+			row['total_issues'] = data.get('total_issues', 0.0)
+
+			for sla_status in self.sla_status_map.values():
+				value = flt(data.get(sla_status), 0.0)
+				row[sla_status] = value
+
+			for metric in self.metrics:
+				value = flt(data.get(scrub(metric)), 0.0)
+				row[scrub(metric)] = value
+
+			self.data.append(row)
+
+	def get_summary_data(self):
+		self.issue_summary_data = frappe._dict()
+
+		for d in self.entries:
+			status = d.status
+			agreement_status = scrub(d.agreement_status)
+
+			if self.filters.based_on == 'Assigned To':
+				if d._assign:
+					for entry in json.loads(d._assign):
+						self.issue_summary_data.setdefault(entry, frappe._dict()).setdefault(status, 0.0)
+						self.issue_summary_data.setdefault(entry, frappe._dict()).setdefault(agreement_status, 0.0)
+						self.issue_summary_data.setdefault(entry, frappe._dict()).setdefault('total_issues', 0.0)
+						self.issue_summary_data[entry][status] += 1
+						self.issue_summary_data[entry][agreement_status] += 1
+						self.issue_summary_data[entry]['total_issues'] += 1
+
+			else:
+				field = self.field_map.get(self.filters.based_on)
+				value = d.get(field)
+				if not value:
+					value = _('Not Specified')
+
+				self.issue_summary_data.setdefault(value, frappe._dict()).setdefault(status, 0.0)
+				self.issue_summary_data.setdefault(value, frappe._dict()).setdefault(agreement_status, 0.0)
+				self.issue_summary_data.setdefault(value, frappe._dict()).setdefault('total_issues', 0.0)
+				self.issue_summary_data[value][status] += 1
+				self.issue_summary_data[value][agreement_status] += 1
+				self.issue_summary_data[value]['total_issues'] += 1
+
+		self.get_metrics_data()
+
+	def get_metrics_data(self):
+		issues = []
+
+		metrics_list = ['avg_response_time', 'avg_first_response_time', 'avg_hold_time',
+			'avg_resolution_time', 'avg_user_resolution_time']
+
+		for entry in self.entries:
+			issues.append(entry.name)
+
+		field = self.field_map.get(self.filters.based_on)
+
+		if issues:
+			if self.filters.based_on == 'Assigned To':
+				assignment_map = frappe._dict()
+				for d in self.entries:
+					if d._assign:
+						for entry in json.loads(d._assign):
+							for metric in metrics_list:
+								self.issue_summary_data.setdefault(entry, frappe._dict()).setdefault(metric, 0.0)
+
+							self.issue_summary_data[entry]['avg_response_time'] += d.get('avg_response_time') or 0.0
+							self.issue_summary_data[entry]['avg_first_response_time'] += d.get('first_response_time') or 0.0
+							self.issue_summary_data[entry]['avg_hold_time'] += d.get('total_hold_time') or 0.0
+							self.issue_summary_data[entry]['avg_resolution_time'] += d.get('resolution_time') or 0.0
+							self.issue_summary_data[entry]['avg_user_resolution_time'] += d.get('user_resolution_time') or 0.0
+
+							if not assignment_map.get(entry):
+								assignment_map[entry] = 0
+							assignment_map[entry] += 1
+
+				for entry in assignment_map:
+					for metric in metrics_list:
+						self.issue_summary_data[entry][metric] /= flt(assignment_map.get(entry))
+
+			else:
+				data = frappe.db.sql("""
+					SELECT
+						{0}, AVG(first_response_time) as avg_frt,
+						AVG(avg_response_time) as avg_resp_time,
+						AVG(total_hold_time) as avg_hold_time,
+						AVG(resolution_time) as avg_resolution_time,
+						AVG(user_resolution_time) as avg_user_resolution_time
+					FROM `tabIssue`
+					WHERE
+						name IN %(issues)s
+					GROUP BY {0}
+				""".format(field), {'issues': issues}, as_dict=1)
+
+				for entry in data:
+					value = entry.get(field)
+					if not value:
+						value = _('Not Specified')
+
+					for metric in metrics_list:
+						self.issue_summary_data.setdefault(value, frappe._dict()).setdefault(metric, 0.0)
+
+					self.issue_summary_data[value]['avg_response_time'] = entry.get('avg_resp_time') or 0.0
+					self.issue_summary_data[value]['avg_first_response_time'] = entry.get('avg_frt') or 0.0
+					self.issue_summary_data[value]['avg_hold_time'] = entry.get('avg_hold_time') or 0.0
+					self.issue_summary_data[value]['avg_resolution_time'] = entry.get('avg_resolution_time') or 0.0
+					self.issue_summary_data[value]['avg_user_resolution_time'] = entry.get('avg_user_resolution_time') or 0.0
+
+	def get_chart_data(self):
+		if not self.data:
+			return None
+
+		labels = []
+		open_issues = []
+		replied_issues = []
+		resolved_issues = []
+		closed_issues = []
+
+		entity = self.filters.based_on
+		entity_field = self.field_map.get(entity)
+		if entity == 'Assigned To':
+			entity_field = 'user'
+
+		for entry in self.data:
+			labels.append(entry.get(entity_field))
+			open_issues.append(entry.get('open'))
+			replied_issues.append(entry.get('replied'))
+			resolved_issues.append(entry.get('resolved'))
+			closed_issues.append(entry.get('closed'))
+
+		self.chart = {
+			'data': {
+				'labels': labels[:30],
+				'datasets': [
+					{
+						'name': 'Open',
+						'values': open_issues[:30]
+					},
+					{
+						'name': 'Replied',
+						'values': replied_issues[:30]
+					},
+					{
+						'name': 'Resolved',
+						'values': resolved_issues[:30]
+					},
+					{
+						'name': 'Closed',
+						'values': closed_issues[:30]
+					}
+				]
+			},
+			'type': 'bar',
+			'barOptions': {
+				'stacked': True
+			}
+		}
+
+	def get_report_summary(self):
+		if not self.data:
+			return None
+
+		open_issues = 0
+		replied = 0
+		resolved = 0
+		closed = 0
+
+		for entry in self.data:
+			open_issues += entry.get('open')
+			replied += entry.get('replied')
+			resolved += entry.get('resolved')
+			closed += entry.get('closed')
+
+		self.report_summary = [
+			{
+				'value': open_issues,
+				'indicator': 'Red',
+				'label': _('Open'),
+				'datatype': 'Int',
+			},
+			{
+				'value': replied,
+				'indicator': 'Grey',
+				'label': _('Replied'),
+				'datatype': 'Int',
+			},
+			{
+				'value': resolved,
+				'indicator': 'Green',
+				'label': _('Resolved'),
+				'datatype': 'Int',
+			},
+			{
+				'value': closed,
+				'indicator': 'Green',
+				'label': _('Closed'),
+				'datatype': 'Int',
+			}
+		]
+
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/telephony/doctype/voice_call_settings/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/telephony/doctype/voice_call_settings/__init__.py
diff --git a/erpnext/non_profit/doctype/membership_settings/test_membership_settings.py b/erpnext/telephony/doctype/voice_call_settings/test_voice_call_settings.py
similarity index 79%
rename from erpnext/non_profit/doctype/membership_settings/test_membership_settings.py
rename to erpnext/telephony/doctype/voice_call_settings/test_voice_call_settings.py
index 2ad7984..85d6add 100644
--- a/erpnext/non_profit/doctype/membership_settings/test_membership_settings.py
+++ b/erpnext/telephony/doctype/voice_call_settings/test_voice_call_settings.py
@@ -6,5 +6,5 @@
 # import frappe
 import unittest
 
-class TestMembershipSettings(unittest.TestCase):
+class TestVoiceCallSettings(unittest.TestCase):
 	pass
diff --git a/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.js b/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.js
new file mode 100644
index 0000000..4a61b61
--- /dev/null
+++ b/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.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('Voice Call Settings', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.json b/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.json
new file mode 100644
index 0000000..25e55a2
--- /dev/null
+++ b/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.json
@@ -0,0 +1,124 @@
+{
+ "actions": [],
+ "autoname": "field:user",
+ "creation": "2020-12-08 16:52:40.590146",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "user",
+  "call_receiving_device",
+  "column_break_3",
+  "greeting_message",
+  "agent_busy_message",
+  "agent_unavailable_message"
+ ],
+ "fields": [
+  {
+   "fieldname": "user",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "User",
+   "options": "User",
+   "permlevel": 1,
+   "reqd": 1,
+   "unique": 1
+  },
+  {
+   "fieldname": "greeting_message",
+   "fieldtype": "Data",
+   "label": "Greeting Message"
+  },
+  {
+   "fieldname": "agent_busy_message",
+   "fieldtype": "Data",
+   "label": "Agent Busy Message"
+  },
+  {
+   "fieldname": "agent_unavailable_message",
+   "fieldtype": "Data",
+   "label": "Agent Unavailable Message"
+  },
+  {
+   "default": "Computer",
+   "fieldname": "call_receiving_device",
+   "fieldtype": "Select",
+   "label": "Call Receiving Device",
+   "options": "Computer\nPhone"
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2020-12-14 18:49:34.600194",
+ "modified_by": "Administrator",
+ "module": "Telephony",
+ "name": "Voice Call Settings",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "All",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "permlevel": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "permlevel": 2,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "permlevel": 2,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "All",
+   "share": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.py b/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.py
new file mode 100644
index 0000000..ad3bbf1
--- /dev/null
+++ b/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.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 VoiceCallSettings(Document):
+	pass
diff --git a/erpnext/templates/generators/item_group.html b/erpnext/templates/generators/item_group.html
index 40a064f..74b2ae3 100644
--- a/erpnext/templates/generators/item_group.html
+++ b/erpnext/templates/generators/item_group.html
@@ -1,5 +1,9 @@
 {% extends "templates/web.html" %}
 
+{% block breadcrumbs %}
+	{% include "templates/includes/breadcrumbs.html" %}
+{% endblock %}
+
 {% block header %}<h1>{{ name }}</h1>{% endblock %}
 
 {% block page_content %}
diff --git a/erpnext/templates/generators/job_opening.html b/erpnext/templates/generators/job_opening.html
index f92e72e..c562db3 100644
--- a/erpnext/templates/generators/job_opening.html
+++ b/erpnext/templates/generators/job_opening.html
@@ -13,10 +13,21 @@
 {%- if description -%}
 <div>{{ description }}</div>
 {% endif %}
+
+{%- if publish_salary_range -%} 
+<div><b>{{_("Salary range per month")}}: </b>{{ frappe.format_value(frappe.utils.flt(lower_range), currency=currency) }} - {{ frappe.format_value(frappe.utils.flt(upper_range), currency=currency) }}</div>
+{% endif %}
+
 <p style='margin-top: 30px'>
-	<a class='btn btn-primary'
+	{%- if job_application_route -%}
+	<a class='btn btn-primary' 
+	href='/{{job_application_route}}?new=1&job_title={{ doc.name }}'>
+	{{ _("Apply Now") }}</a>
+	{% else %}
+	<a class='btn btn-primary' 
 	href='/job_application?new=1&job_title={{ doc.name }}'>
 	{{ _("Apply Now") }}</a>
+	{% endif %}
 </p>
 
 {% endblock %}
diff --git a/erpnext/templates/includes/macros.html b/erpnext/templates/includes/macros.html
index ea6b00f..5d8ee5c 100644
--- a/erpnext/templates/includes/macros.html
+++ b/erpnext/templates/includes/macros.html
@@ -40,7 +40,7 @@
 		<div class="col-md-{{ section.column_value }} mb-4">
 			<div class="card h-100 justify-content-between">
 				{% if card.image %}
-				<div class="website-image-lazy" data-class="card-img-top h-100" data-src="{{ card.image }}" data-alt="{{ card.title }}"></div>
+				<div class="website-image-lazy" data-class="card-img-top h-75" data-src="{{ card.image }}" data-alt="{{ card.title }}"></div>
 				{% endif %}
 				<div class="card-body">
 					<h5 class="card-title">{{ card.title }}</h5>
diff --git a/erpnext/templates/pages/cart.html b/erpnext/templates/pages/cart.html
index 3033d15..876eaea 100644
--- a/erpnext/templates/pages/cart.html
+++ b/erpnext/templates/pages/cart.html
@@ -47,6 +47,9 @@
 
 	{% if doc.items %}
 		<div class="place-order-container">
+			<a class="btn btn-primary-light mr-2" href="/all-products">
+				{{ _("Continue Shopping") }}
+			</a>
 			{% if cart_settings.enable_checkout %}
 				<button class="btn btn-primary btn-place-order" type="button">
 					{{ _("Place Order") }}
diff --git a/erpnext/www/all-products/index.py b/erpnext/www/all-products/index.py
index 0394e4b..7d7793a 100644
--- a/erpnext/www/all-products/index.py
+++ b/erpnext/www/all-products/index.py
@@ -15,6 +15,9 @@
 
 	context.items = get_products_for_website(field_filters, attribute_filters, search)
 
+	# Add homepage as parent
+	context.parents = [{"name": frappe._("Home"), "route":"/"}]
+
 	product_settings = get_product_settings()
 	context.field_filters = get_field_filter_data() \
 		if product_settings.enable_field_filters else []
diff --git a/requirements.txt b/requirements.txt
index c448ebc..4511aa5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,7 +2,7 @@
 frappe
 gocardless-pro==1.11.0
 googlemaps==3.1.1
-pandas==1.0.5
+pandas>=1.0.5
 plaid-python==6.0.0
 pycountry==19.8.18
 PyGithub==1.44.1