Merge branch 'version-13-beta-pre-release' into version-13-beta
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index b122e5f..78e87c8 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.13'
+__version__ = '13.0.0-beta.14'
 
 def get_default_company(user=None):
 	'''Get default company for user'''
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index f5c488d..b5f6a40 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -92,14 +92,16 @@
 		});
 
 		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"];
 			}
@@ -128,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;
@@ -281,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() {
@@ -603,12 +605,22 @@
 			{fieldtype:"Column Break"},
 			{fieldtype:"Float", label: __("Less Than Amount"), fieldname:"outstanding_amt_less_than"},
 			{fieldtype:"Section Break"},
+			{fieldtype:"Link", label:__("Cost Center"), fieldname:"cost_center", options:"Cost Center",
+                "get_query": function() {
+                    return {
+                        "filters": {"company": frm.doc.company}
+					}
+				}
+			},
+			{fieldtype:"Column Break"},
+			{fieldtype:"Section Break"},
 			{fieldtype:"Check", label: __("Allocate Payment Amount"), fieldname:"allocate_payment_amount", default:1},
 		];
 
 		frappe.prompt(fields, function(filters){
 			frappe.flags.allocate_payment_amount = true;
 			frm.events.validate_filters_data(frm, filters);
+			frm.doc.cost_center = filters.cost_center;
 			frm.events.get_outstanding_documents(frm, filters);
 		}, __("Filters"), __("Get Outstanding Documents"));
 	},
@@ -705,7 +717,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)
@@ -748,7 +761,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;
@@ -905,6 +919,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) {
@@ -1056,11 +1076,6 @@
 								frm.set_value("paid_from_account_balance", r.message.paid_from_account_balance);
 								frm.set_value("paid_to_account_balance", r.message.paid_to_account_balance);
 								frm.set_value("party_balance", r.message.party_balance);
-							},
-							() => {
-								if(frm.doc.payment_type != "Internal") {
-									frm.clear_table("references");
-								}
 							}
 						]);
 
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/pos_opening_entry/pos_opening_entry.py b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
index cb5b3a5..0023a84 100644
--- a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
+++ b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
@@ -20,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/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
index d08a854..81890d5 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
@@ -44,6 +44,14 @@
   "column_break_21",
   "min_amt",
   "max_amt",
+  "product_discount_scheme_section",
+  "same_item",
+  "free_item",
+  "free_qty",
+  "free_item_rate",
+  "column_break_42",
+  "free_item_uom",
+  "is_recursive",
   "section_break_23",
   "valid_from",
   "valid_upto",
@@ -62,13 +70,6 @@
   "discount_amount",
   "discount_percentage",
   "for_price_list",
-  "product_discount_scheme_section",
-  "same_item",
-  "free_item",
-  "free_qty",
-  "column_break_51",
-  "free_item_uom",
-  "free_item_rate",
   "section_break_13",
   "threshold_percentage",
   "priority",
@@ -460,10 +461,6 @@
    "label": "Qty"
   },
   {
-   "fieldname": "column_break_51",
-   "fieldtype": "Column Break"
-  },
-  {
    "fieldname": "free_item_uom",
    "fieldtype": "Link",
    "label": "UOM",
@@ -553,19 +550,33 @@
    "fieldname": "promotional_scheme",
    "fieldtype": "Link",
    "label": "Promotional Scheme",
-   "options": "Promotional Scheme"
+   "no_copy": 1,
+   "options": "Promotional Scheme",
+   "print_hide": 1,
+   "read_only": 1
   },
   {
    "description": "Simple Python Expression, Example: territory != 'All Territories'",
    "fieldname": "condition",
    "fieldtype": "Code",
    "label": "Condition"
+  },
+  {
+   "fieldname": "column_break_42",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "description": "Discounts to be applied in sequential ranges like buy 1 get 1, buy 2 get 2, buy 3 get 3 and so on",
+   "fieldname": "is_recursive",
+   "fieldtype": "Check",
+   "label": "Is Recursive"
   }
  ],
  "icon": "fa fa-gift",
  "idx": 1,
  "links": [],
- "modified": "2020-12-04 00:36:24.698219",
+ "modified": "2021-03-06 22:01:24.840422",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Pricing Rule",
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index 0565264..9a3ea27 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -237,6 +237,7 @@
 		"doctype": args.doctype,
 		"has_margin": False,
 		"name": args.name,
+		"free_item_data": [],
 		"parent": args.parent,
 		"parenttype": args.parenttype,
 		"child_docname": args.get('child_docname')
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index f28cee7..ef9aad5 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -328,6 +328,21 @@
 		self.assertEquals(item.discount_amount, 110)
 		self.assertEquals(item.rate, 990)
 
+	def test_pricing_rule_with_margin_and_discount_amount(self):
+		frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
+		make_pricing_rule(selling=1, margin_type="Percentage", margin_rate_or_amount=10,
+			rate_or_discount="Discount Amount", discount_amount=110)
+		si = create_sales_invoice(do_not_save=True)
+		si.items[0].price_list_rate = 1000
+		si.payment_schedule = []
+		si.insert(ignore_permissions=True)
+
+		item = si.items[0]
+		self.assertEquals(item.margin_rate_or_amount, 10)
+		self.assertEquals(item.rate_with_margin, 1100)
+		self.assertEquals(item.discount_amount, 110)
+		self.assertEquals(item.rate, 990)
+
 	def test_pricing_rule_for_product_discount_on_same_item(self):
 		frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
 		test_record = {
@@ -560,6 +575,7 @@
 		"margin_rate_or_amount": args.margin_rate_or_amount or 0.0,
 		"condition": args.condition or '',
 		"priority": 1,
+		"discount_amount": args.discount_amount or 0.0,
 		"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 d163335..c676abd 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -367,7 +367,7 @@
 
 	if items and doc.get("items"):
 		for row in doc.get('items'):
-			if row.get(apply_on) not in items: continue
+			if (row.get(apply_on) or args.get(apply_on)) not in items: continue
 
 			if pr_doc.mixed_conditions:
 				amt = args.get('qty') * args.get("price_list_rate")
@@ -479,7 +479,7 @@
 
 				doc.calculate_taxes_and_totals()
 			elif d.price_or_product_discount == 'Product':
-				item_details = frappe._dict({'parenttype': doc.doctype})
+				item_details = frappe._dict({'parenttype': doc.doctype, 'free_item_data': []})
 				get_product_discount_rule(d, item_details, doc=doc)
 				apply_pricing_rule_for_free_items(doc, item_details.free_item_data)
 				doc.set_missing_values()
@@ -508,9 +508,16 @@
 		frappe.throw(_("Free item not set in the pricing rule {0}")
 			.format(get_link_to_form("Pricing Rule", pricing_rule.name)))
 
-	item_details.free_item_data = {
+	qty = pricing_rule.free_qty or 1
+	if pricing_rule.is_recursive:
+		transaction_qty = args.get('qty') if args else doc.total_qty
+		if transaction_qty:
+			qty = flt(transaction_qty) * qty
+
+	free_item_data_args = {
 		'item_code': free_item,
-		'qty': pricing_rule.free_qty or 1,
+		'qty': qty,
+		'pricing_rules': pricing_rule.name,
 		'rate': pricing_rule.free_item_rate or 0,
 		'price_list_rate': pricing_rule.free_item_rate or 0,
 		'is_free_item': 1
@@ -519,24 +526,26 @@
 	item_data = frappe.get_cached_value('Item', free_item, ['item_name',
 		'description', 'stock_uom'], as_dict=1)
 
-	item_details.free_item_data.update(item_data)
-	item_details.free_item_data['uom'] = pricing_rule.free_item_uom or item_data.stock_uom
-	item_details.free_item_data['conversion_factor'] = get_conversion_factor(free_item,
-		item_details.free_item_data['uom']).get("conversion_factor", 1)
+	free_item_data_args.update(item_data)
+	free_item_data_args['uom'] = pricing_rule.free_item_uom or item_data.stock_uom
+	free_item_data_args['conversion_factor'] = get_conversion_factor(free_item,
+		free_item_data_args['uom']).get("conversion_factor", 1)
 
 	if item_details.get("parenttype") == 'Purchase Order':
-		item_details.free_item_data['schedule_date'] = doc.schedule_date if doc else today()
+		free_item_data_args['schedule_date'] = doc.schedule_date if doc else today()
 
 	if item_details.get("parenttype") == 'Sales Order':
-		item_details.free_item_data['delivery_date'] = doc.delivery_date if doc else today()
+		free_item_data_args['delivery_date'] = doc.delivery_date if doc else today()
+
+	item_details.free_item_data.append(free_item_data_args)
 
 def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False):
-	if pricing_rule_args.get('item_code'):
-		items = [d.item_code for d in doc.items
-			if d.item_code == (pricing_rule_args.get("item_code")) and d.is_free_item]
+	if pricing_rule_args:
+		items = tuple([(d.item_code, d.pricing_rules) for d in doc.items if d.is_free_item])
 
-		if not items:
-			doc.append('items', pricing_rule_args)
+		for args in pricing_rule_args:
+			if not items or (args.get('item_code'), args.get('pricing_rules')) not in items:
+				doc.append('items', args)
 
 def get_pricing_rule_items(pr_doc):
 	apply_on_data = []
diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py
index 89f7238..523e9ee 100644
--- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py
+++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py
@@ -12,16 +12,16 @@
 pricing_rule_fields = ['apply_on', 'mixed_conditions', 'is_cumulative', 'other_item_code', 'other_item_group'
 	'apply_rule_on_other', 'other_brand', 'selling', 'buying', 'applicable_for', 'valid_from',
 	'valid_upto', 'customer', 'customer_group', 'territory', 'sales_partner', 'campaign', 'supplier',
-	'supplier_group', 'company', 'currency']
+	'supplier_group', 'company', 'currency', 'apply_multiple_pricing_rules']
 
 other_fields = ['min_qty', 'max_qty', 'min_amt',
 	'max_amt', 'priority','warehouse', 'threshold_percentage', 'rule_description']
 
 price_discount_fields = ['rate_or_discount', 'apply_discount_on', 'apply_discount_on_rate',
-	'rate', 'discount_amount', 'discount_percentage', 'validate_applied_rule']
+	'rate', 'discount_amount', 'discount_percentage', 'validate_applied_rule', 'apply_multiple_pricing_rules']
 
 product_discount_fields = ['free_item', 'free_qty', 'free_item_uom',
-	'free_item_rate', 'same_item']
+	'free_item_rate', 'same_item', 'is_recursive', 'apply_multiple_pricing_rules']
 
 class PromotionalScheme(Document):
 	def validate(self):
diff --git a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json
index 224b8de..795fb1c 100644
--- a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json
+++ b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json
@@ -1,792 +1,181 @@
 {
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
+ "actions": [],
  "creation": "2019-03-24 14:48:59.649168",
- "custom": 0,
- "docstatus": 0,
  "doctype": "DocType",
- "document_type": "",
  "editable_grid": 1,
  "engine": "InnoDB",
+ "field_order": [
+  "disable",
+  "apply_multiple_pricing_rules",
+  "column_break_2",
+  "rule_description",
+  "section_break_2",
+  "min_qty",
+  "max_qty",
+  "column_break_3",
+  "min_amount",
+  "max_amount",
+  "section_break_6",
+  "rate_or_discount",
+  "column_break_10",
+  "rate",
+  "discount_amount",
+  "discount_percentage",
+  "section_break_11",
+  "warehouse",
+  "threshold_percentage",
+  "validate_applied_rule",
+  "column_break_14",
+  "priority",
+  "apply_discount_on_rate"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
+   "default": "0",
    "fieldname": "disable",
    "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": "Disable",
-   "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": "Disable"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "column_break_2",
-   "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
+   "fieldtype": "Column Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "rule_description",
    "fieldtype": "Small 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": "Rule Description",
-   "length": 0,
    "no_copy": 1,
-   "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
+   "reqd": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "section_break_2",
-   "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
+   "fieldtype": "Section Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
    "columns": 1,
    "default": "0",
    "fieldname": "min_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": "Min 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
+   "label": "Min Qty"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
    "columns": 1,
    "default": "0",
    "fieldname": "max_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": "Max 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
+   "label": "Max Qty"
   },
   {
-   "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
+   "fieldtype": "Column Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "default": "0",
    "fieldname": "min_amount",
    "fieldtype": "Currency",
-   "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": "Min Amount",
-   "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": "Min Amount"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "default": "0",
-   "depends_on": "",
    "fieldname": "max_amount",
    "fieldtype": "Currency",
-   "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": "Max Amount",
-   "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": "Max Amount"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "depends_on": "",
    "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,
-   "label": "",
-   "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
+   "fieldtype": "Section Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "default": "Discount Percentage",
-   "depends_on": "",
    "fieldname": "rate_or_discount",
    "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": 0,
    "label": "Discount Type",
-   "length": 0,
-   "no_copy": 0,
-   "options": "\nRate\nDiscount Percentage\nDiscount Amount",
-   "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
+   "options": "\nRate\nDiscount Percentage\nDiscount Amount"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "depends_on": "",
    "fieldname": "column_break_10",
-   "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
+   "fieldtype": "Column Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
    "columns": 2,
    "depends_on": "eval:doc.rate_or_discount==\"Rate\"",
    "fieldname": "rate",
    "fieldtype": "Currency",
-   "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": "Rate",
-   "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": "Rate"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "depends_on": "eval:doc.rate_or_discount==\"Discount Amount\"",
    "fieldname": "discount_amount",
    "fieldtype": "Currency",
-   "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": "Discount Amount",
-   "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": "Discount Amount"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "depends_on": "eval:doc.rate_or_discount==\"Discount Percentage\"",
    "fieldname": "discount_percentage",
    "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": "Discount Percentage",
-   "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": "Discount Percentage"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "section_break_11",
-   "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
+   "fieldtype": "Section Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "warehouse",
    "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": "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
+   "options": "Warehouse"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "threshold_percentage",
    "fieldtype": "Percent",
-   "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": "Threshold for Suggestion",
-   "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": "Threshold for Suggestion"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "default": "1",
    "fieldname": "validate_applied_rule",
    "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": "Validate Applied Rule",
-   "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": "Validate Applied Rule"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "column_break_14",
-   "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
+   "fieldtype": "Column Break"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "priority",
    "fieldtype": "Select",
-   "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": "Priority",
-   "length": 0,
-   "no_copy": 0,
-   "options": "\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20",
-   "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
+   "options": "\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
+   "default": "0",
    "depends_on": "priority",
    "fieldname": "apply_multiple_pricing_rules",
    "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": "Apply Multiple Pricing Rules",
-   "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": "Apply Multiple Pricing Rules"
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "default": "0",
    "depends_on": "eval:in_list(['Discount Percentage', 'Discount Amount'], doc.rate_or_discount) && doc.apply_multiple_pricing_rules",
    "fieldname": "apply_discount_on_rate",
    "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": "Apply Discount on Rate",
-   "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": "Apply Discount on Rate"
   }
  ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
+ "index_web_pages_for_search": 1,
  "istable": 1,
- "max_attachments": 0,
- "modified": "2019-03-24 14:48:59.649168",
+ "links": [],
+ "modified": "2021-03-07 11:56:23.424137",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Promotional Scheme Price Discount",
- "name_case": "",
  "owner": "Administrator",
  "permissions": [],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
  "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
+ "sort_order": "DESC"
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json
index 72d53bf..3eab515 100644
--- a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json
+++ b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json
@@ -1,10 +1,12 @@
 {
+ "actions": [],
  "creation": "2019-03-24 14:48:59.649168",
  "doctype": "DocType",
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
   "disable",
+  "apply_multiple_pricing_rules",
   "column_break_2",
   "rule_description",
   "section_break_1",
@@ -25,7 +27,7 @@
   "threshold_percentage",
   "column_break_15",
   "priority",
-  "apply_multiple_pricing_rules"
+  "is_recursive"
  ],
  "fields": [
   {
@@ -152,10 +154,19 @@
    "fieldname": "apply_multiple_pricing_rules",
    "fieldtype": "Check",
    "label": "Apply Multiple Pricing Rules"
+  },
+  {
+   "default": "0",
+   "description": "Discounts to be applied in sequential ranges like buy 1 get 1, buy 2 get 2, buy 3 get 3 and so on",
+   "fieldname": "is_recursive",
+   "fieldtype": "Check",
+   "label": "Is Recursive"
   }
  ],
+ "index_web_pages_for_search": 1,
  "istable": 1,
- "modified": "2019-07-21 00:00:56.674284",
+ "links": [],
+ "modified": "2021-03-06 21:58:18.162346",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Promotional Scheme Product Discount",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 06aa20b..66a8e20 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -524,7 +524,7 @@
 	},
 
 	onload: function(frm) {
-		if(frm.doc.__onload) {
+		if(frm.doc.__onload && frm.is_new()) {
 			if(frm.doc.supplier) {
 				frm.doc.apply_tds = frm.doc.__onload.supplier_tds ? 1 : 0;
 			}
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 451c936..3ff0efe 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -58,6 +58,7 @@
   "rejected_warehouse",
   "col_break_warehouse",
   "set_from_warehouse",
+  "supplier_warehouse",
   "is_subcontracted",
   "items_section",
   "update_stock",
@@ -1350,7 +1351,7 @@
    "options": "Company"
   },
   {
-   "depends_on": "eval:doc.update_stock && (doc.is_subcontracted==\"Yes\" || doc.is_internal_supplier)",
+   "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",
@@ -1360,13 +1361,24 @@
    "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-26 20:49:03.305063",
+ "modified": "2021-03-09 21:56:28.748582",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 7cd1828..979231a 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1166,10 +1166,12 @@
 
 	def test_create_so_with_margin(self):
 		si = create_sales_invoice(item_code="_Test Item", qty=1, do_not_submit=True)
-		price_list_rate = 100
+		price_list_rate = flt(100) * flt(si.plc_conversion_rate)
 		si.items[0].price_list_rate = price_list_rate
 		si.items[0].margin_type = 'Percentage'
 		si.items[0].margin_rate_or_amount = 25
+		si.items[0].discount_amount = 0.0
+		si.items[0].discount_percentage = 0.0
 		si.save()
 		self.assertEqual(si.get("items")[0].rate, flt((price_list_rate*25)/100 + price_list_rate))
 
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 76f3c50..0c4a422 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -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'))
@@ -381,9 +399,9 @@
 			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, accounts)
-			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
 
@@ -452,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/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index 7dfce85..14efa1f 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -51,7 +51,11 @@
 			"from_date": start_date
 		})
 
-		to_date = add_months(start_date, months_to_add)
+		if i==0 and filter_based_on == 'Date Range':
+			to_date = add_months(get_first_day(start_date), months_to_add)
+		else:
+			to_date = add_months(start_date, months_to_add)
+
 		start_date = to_date
 
 		# Subtract one day from to_date, as it may be first day in next fiscal year or month
diff --git a/erpnext/change_log/v13/v13_0_0-beta_14.md b/erpnext/change_log/v13/v13_0_0-beta_14.md
new file mode 100644
index 0000000..1fa4376
--- /dev/null
+++ b/erpnext/change_log/v13/v13_0_0-beta_14.md
@@ -0,0 +1,25 @@
+## Version 13.0.0 Beta 14 Release Notes
+### Fixes and Enhancements
+
+- Repost incompleted backdated transactions ([#24991](https://github.com/frappe/erpnext/pull/24991))
+- Revert stock balance value calculation ([#24957](https://github.com/frappe/erpnext/pull/24957))
+- Allow user to update exchange rate in Multi-currency LCV ([#24947](https://github.com/frappe/erpnext/pull/24947))
+- Added correct path in hooks ([#24865](https://github.com/frappe/erpnext/pull/24865))
+- Unequal debit and credit issue on RCM Invoice ([#24838](https://github.com/frappe/erpnext/pull/24838))
+- Period list for exponential smoothing forecasting report ([#24983](https://github.com/frappe/erpnext/pull/24983))
+- POS Opening Entry with empty balance detail rows ([#24891](https://github.com/frappe/erpnext/pull/24891))
+- Use account_name only in consolidated report ([#24840](https://github.com/frappe/erpnext/pull/24840))
+- Validation of job card in stock entry ([#24882](https://github.com/frappe/erpnext/pull/24882))
+- Added supplier warehouse field back again ([#24827](https://github.com/frappe/erpnext/pull/24827))
+- Don't throw exception on invoice lines when there is no item_cod… ([#24864](https://github.com/frappe/erpnext/pull/24864))
+- Incorrect Nil Exempt and Non GST amount in GSTR3B report ([#24918](https://github.com/frappe/erpnext/pull/24918))
+- Payment References on adding Cost Center in PE and Report Issue Summary fix for V13 beta pre-release ([#24951](https://github.com/frappe/erpnext/pull/24951))
+- TDS check getting checked after reload ([#24973](https://github.com/frappe/erpnext/pull/24973))
+- Membership and Donation API fixes ([#24900](https://github.com/frappe/erpnext/pull/24900))
+- Serial no trim issue ([#24981](https://github.com/frappe/erpnext/pull/24981))
+- Add method for regional round off account back ([#24894](https://github.com/frappe/erpnext/pull/24894))
+- Allow zero valuation in stock reconciliation ([#24985](https://github.com/frappe/erpnext/pull/24985))
+- Simplified logic for additional salary ([#24907](https://github.com/frappe/erpnext/pull/24907))
+- Allow to select item code in batch naming ([#24825](https://github.com/frappe/erpnext/pull/24825))
+- 80G Certificates and Donations ([#24848](https://github.com/frappe/erpnext/pull/24848))
+- Membership renewal validation (#24963) ([#24964](https://github.com/frappe/erpnext/pull/24964))
\ No newline at end of file
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index e6b14c2..2564379 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -25,7 +25,8 @@
 
 class AccountMissingError(frappe.ValidationError): pass
 
-force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules")
+force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate",
+	"pricing_rules", "weight_per_unit", "weight_uom", "total_weight")
 
 class AccountsController(TransactionBase):
 	def __init__(self, *args, **kwargs):
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index ab1f027..305a162 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -288,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:
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index cb44b73..f92e884 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -405,7 +405,7 @@
 	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
+				d.stock_uom_rate = d.rate / (d.conversion_factor or 1)
 
 	def validate_internal_transfer(self):
 		if self.doctype in ('Sales Invoice', 'Delivery Note', 'Purchase Invoice', 'Purchase Receipt') \
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 6c7eb92..f976b17 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -111,7 +111,12 @@
 					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"))
-						item.discount_amount = item.rate_with_margin - item.rate
+
+						if item.discount_amount and not item.discount_percentage:
+							item.rate = item.rate_with_margin - item.discount_amount
+						else:
+							item.discount_amount = item.rate_with_margin - item.rate
+
 					elif flt(item.price_list_rate) > 0:
 						item.discount_amount = item.price_list_rate - item.rate
 				elif flt(item.price_list_rate) > 0 and not item.discount_amount:
@@ -779,7 +784,7 @@
 		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:
+			elif not d.exchange_rate:
 				d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
 					account_currency=d.account_currency, company=self.doc.company)
 
diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py
index cc75a0a..148c1a6 100644
--- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py
+++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py
@@ -117,7 +117,7 @@
 			return response
 		except Exception as e:
 			delay = math.pow(4, x) * 125
-			frappe.log_error(message=e, title=str(mws_method))
+			frappe.log_error(message=e, title=f'Method "{mws_method.__name__}" failed')
 			time.sleep(delay)
 			continue
 
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 1c20555..0401be4 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -320,6 +320,7 @@
 		"erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts",
 		"erpnext.support.doctype.issue.issue.set_service_level_agreement_variance",
 		"erpnext.erpnext_integrations.connectors.shopify_connection.sync_old_orders",
+		"erpnext.stock.doctype.repost_item_valuation.repost_item_valuation.repost_entries"
 	],
 	"daily": [
 		"erpnext.stock.reorder_item.reorder_item",
@@ -357,13 +358,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"
 	]
 }
 
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index ec28eb7..d2ac712 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -255,6 +255,9 @@
 				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
+				if data.get("workstation") != self.workstation:
+					# workstations can change in a job card
+					data.workstation = self.workstation
 
 		wo.flags.ignore_validate_update_after_submit = True
 		wo.update_operation_status()
@@ -267,6 +270,17 @@
 			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
@@ -279,7 +293,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)
@@ -415,11 +430,13 @@
 def make_stock_entry(source_name, target_doc=None):
 	def update_item(obj, target, source_parent):
 		target.t_warehouse = source_parent.wip_warehouse
+		target.conversion_factor = 1
 
 	def set_missing_values(source, target):
 		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()
@@ -437,9 +454,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/material_request_plan_item/material_request_plan_item.json b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
index f93b244..6c60bbd 100644
--- a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
+++ b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
@@ -11,10 +11,14 @@
   "from_warehouse",
   "warehouse",
   "column_break_4",
+  "required_bom_qty",
   "quantity",
   "uom",
   "projected_qty",
   "actual_qty",
+  "ordered_qty",
+  "reserved_qty_for_production",
+  "safety_stock",
   "item_details",
   "description",
   "min_order_qty",
@@ -129,11 +133,40 @@
    "fieldtype": "Link",
    "label": "From Warehouse",
    "options": "Warehouse"
+  },
+  {
+   "fetch_from": "item_code.safety_stock",
+   "fieldname": "safety_stock",
+   "fieldtype": "Float",
+   "label": "Safety Stock",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "ordered_qty",
+   "fieldtype": "Float",
+   "label": "Ordered Qty",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "reserved_qty_for_production",
+   "fieldtype": "Float",
+   "label": "Reserved Qty for Production",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "required_bom_qty",
+   "fieldtype": "Float",
+   "label": "Required Qty as per BOM",
+   "no_copy": 1,
+   "read_only": 1
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2020-02-03 12:22:29.913302",
+ "modified": "2021-03-26 12:41:13.013149",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Material Request Plan Item",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index b723387..15ec620 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -251,7 +251,8 @@
 
 	get_items_for_material_requests: function(frm, warehouses) {
 		const set_fields = ['actual_qty', 'item_code','item_name', 'description', 'uom', 'from_warehouse',
-			'min_order_qty', 'quantity', 'sales_order', 'warehouse', 'projected_qty', 'material_request_type'];
+			'min_order_qty', 'required_bom_qty', 'quantity', 'sales_order', 'warehouse', 'projected_qty', 'ordered_qty',
+			'reserved_qty_for_production', 'material_request_type'];
 
 		frappe.call({
 			method: "erpnext.manufacturing.doctype.production_plan.production_plan.get_items_for_material_requests",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index 7daf706..f114700 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -32,6 +32,7 @@
   "material_request_planning",
   "include_non_stock_items",
   "include_subcontracted_items",
+  "include_safety_stock",
   "ignore_existing_ordered_qty",
   "column_break_25",
   "for_warehouse",
@@ -309,13 +310,19 @@
    "fieldtype": "Select",
    "label": "Sales Order Status",
    "options": "\nTo Deliver and Bill\nTo Bill\nTo Deliver"
+  },
+  {
+   "default": "0",
+   "fieldname": "include_safety_stock",
+   "fieldtype": "Check",
+   "label": "Include Safety Stock in Required Qty Calculation"
   }
  ],
  "icon": "fa fa-calendar",
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-11-10 18:01:54.991970",
+ "modified": "2021-03-08 11:17:25.470147",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Production Plan",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 3833e86..2e6569f 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -434,12 +434,14 @@
 	if isinstance(doc, string_types):
 		doc = frappe._dict(json.loads(doc))
 
-	item_list = [['Item Code', 'Description', 'Stock UOM', 'Required Qty', 'Warehouse',
-		'projected Qty', 'Actual Qty']]
+	item_list = [['Item Code', 'Description', 'Stock UOM', 'Warehouse', 'Required Qty as per BOM',
+		'Projected Qty', 'Actual Qty', 'Ordered Qty', 'Reserved Qty for Production',
+		'Safety Stock', 'Required Qty']]
 
 	for d in get_items_for_material_requests(doc):
-		item_list.append([d.get('item_code'), d.get('description'), d.get('stock_uom'), d.get('quantity'),
-			d.get('warehouse'), d.get('projected_qty'), d.get('actual_qty')])
+		item_list.append([d.get('item_code'), d.get('description'), d.get('stock_uom'), d.get('warehouse'),
+			d.get('required_bom_qty'), d.get('projected_qty'), d.get('actual_qty'), d.get('ordered_qty'),
+			d.get('reserved_qty_for_production'), d.get('safety_stock'), d.get('quantity')])
 
 		if not doc.get('for_warehouse'):
 			row = {'item_code': d.get('item_code')}
@@ -447,8 +449,9 @@
 				if d.get("warehouse") == bin_dict.get('warehouse'):
 					continue
 
-				item_list.append(['', '', '', '', bin_dict.get('warehouse'),
-					bin_dict.get('projected_qty', 0), bin_dict.get('actual_qty', 0)])
+				item_list.append(['', '', '', bin_dict.get('warehouse'), '',
+					bin_dict.get('projected_qty', 0), bin_dict.get('actual_qty', 0),
+					bin_dict.get('ordered_qty', 0), bin_dict.get('reserved_qty_for_production', 0)])
 
 	build_csv_response(item_list, doc.name)
 
@@ -482,7 +485,7 @@
 			ifnull(%(parent_qty)s * sum(bom_item.stock_qty/ifnull(bom.quantity, 1)) * %(planned_qty)s, 0) as qty,
 			item.is_sub_contracted_item as is_sub_contracted, bom_item.source_warehouse,
 			item.default_bom as default_bom, bom_item.description as description,
-			bom_item.stock_uom as stock_uom, item.min_order_qty as min_order_qty,
+			bom_item.stock_uom as stock_uom, item.min_order_qty as min_order_qty, item.safety_stock as safety_stock,
 			item_default.default_warehouse, item.purchase_uom, item_uom.conversion_factor
 		FROM
 			`tabBOM Item` bom_item
@@ -518,8 +521,8 @@
 						include_non_stock_items, include_subcontracted_items, d.qty)
 	return item_details
 
-def get_material_request_items(row, sales_order,
-	company, ignore_existing_ordered_qty, warehouse, bin_dict):
+def get_material_request_items(row, sales_order, company,
+	ignore_existing_ordered_qty, include_safety_stock, warehouse, bin_dict):
 	total_qty = row['qty']
 
 	required_qty = 0
@@ -543,17 +546,24 @@
 	if frappe.db.get_value("UOM", row['purchase_uom'], "must_be_whole_number"):
 		required_qty = ceil(required_qty)
 
+	if include_safety_stock:
+		required_qty += flt(row['safety_stock'])
+
 	if required_qty > 0:
 		return {
 			'item_code': row.item_code,
 			'item_name': row.item_name,
 			'quantity': required_qty,
+			'required_bom_qty': total_qty,
 			'description': row.description,
 			'stock_uom': row.get("stock_uom"),
 			'warehouse': warehouse or row.get('source_warehouse') \
 				or row.get('default_warehouse') or item_group_defaults.get("default_warehouse"),
+			'safety_stock': row.safety_stock,
 			'actual_qty': bin_dict.get("actual_qty", 0),
 			'projected_qty': bin_dict.get("projected_qty", 0),
+			'ordered_qty': bin_dict.get("ordered_qty", 0),
+			'reserved_qty_for_production': bin_dict.get("reserved_qty_for_production", 0),
 			'min_order_qty': row['min_order_qty'],
 			'material_request_type': row.get("default_material_request_type"),
 			'sales_order': sales_order,
@@ -620,7 +630,8 @@
 		""".format(lft, rgt, company)
 
 	return frappe.db.sql(""" select ifnull(sum(projected_qty),0) as projected_qty,
-		ifnull(sum(actual_qty),0) as actual_qty, warehouse from `tabBin`
+		ifnull(sum(actual_qty),0) as actual_qty, ifnull(sum(ordered_qty),0) as ordered_qty,
+		ifnull(sum(reserved_qty_for_production),0) as reserved_qty_for_production, warehouse from `tabBin`
 		where item_code = %(item_code)s {conditions}
 		group by item_code, warehouse
 	""".format(conditions=conditions), { "item_code": row['item_code'] }, as_dict=1)
@@ -660,6 +671,7 @@
 
 	company = doc.get('company')
 	ignore_existing_ordered_qty = doc.get('ignore_existing_ordered_qty')
+	include_safety_stock = doc.get('include_safety_stock')
 
 	so_item_details = frappe._dict()
 	for data in po_items:
@@ -711,6 +723,7 @@
 					'description' : item_master.description,
 					'stock_uom' : item_master.stock_uom,
 					'conversion_factor' : conversion_factor,
+					'safety_stock': item_master.safety_stock
 				}
 			)
 
@@ -732,7 +745,7 @@
 
 			if details.qty > 0:
 				items = get_material_request_items(details, sales_order, company,
-					ignore_existing_ordered_qty, warehouse, bin_dict)
+					ignore_existing_ordered_qty, include_safety_stock, warehouse, bin_dict)
 				if items:
 					mr_items.append(items)
 
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json
index 585a09d..cd9edee 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.json
+++ b/erpnext/manufacturing/doctype/work_order/work_order.json
@@ -333,8 +333,7 @@
    "fieldname": "operations",
    "fieldtype": "Table",
    "label": "Operations",
-   "options": "Work Order Operation",
-   "read_only": 1
+   "options": "Work Order Operation"
   },
   {
    "depends_on": "operations",
@@ -496,7 +495,7 @@
  "image_field": "image",
  "is_submittable": 1,
  "links": [],
- "modified": "2020-05-05 19:32:43.323054",
+ "modified": "2021-03-16 13:27:51.116484",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Work Order",
diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
index 2ca9f16..fc27d35 100644
--- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
+++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py
@@ -61,7 +61,7 @@
 
 		from_date = add_years(self.filters.from_date, cint(self.filters.no_of_years) * -1)
 		self.period_list = get_period_list(from_date, self.filters.to_date,
-			from_date, self.filters.to_date, None, self.filters.periodicity, ignore_fiscal_year=True)
+			from_date, self.filters.to_date, "Date Range", self.filters.periodicity, ignore_fiscal_year=True)
 
 		order_data = self.get_data_for_forecast() or []
 
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..6a2a06d
--- /dev/null
+++ b/erpnext/non_profit/doctype/donation/donation.py
@@ -0,0 +1,219 @@
+# -*- 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
+
+		# to avoid capturing subscription payments as donations
+		if payment.description and 'subscription' in str(payment.description).lower():
+			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) / 100, # Convert to rupees from paise
+		'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.py b/erpnext/non_profit/doctype/member/member.py
index 04b99f9..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
 
@@ -26,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({})
@@ -40,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
 		}
 
diff --git a/erpnext/non_profit/doctype/membership/membership.js b/erpnext/non_profit/doctype/membership/membership.js
index 573ac33..3187204 100644
--- a/erpnext/non_profit/doctype/membership/membership.js
+++ b/erpnext/non_profit/doctype/membership/membership.js
@@ -3,7 +3,7 @@
 
 frappe.ui.form.on('Membership', {
 	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.set_df_property("razorpay_details_section", "hidden", false);
 		})
 	},
@@ -26,7 +26,7 @@
 			});
 		});
 
-		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();
diff --git a/erpnext/non_profit/doctype/membership/membership.json b/erpnext/non_profit/doctype/membership/membership.json
index 6da053f..11d32f9 100644
--- a/erpnext/non_profit/doctype/membership/membership.json
+++ b/erpnext/non_profit/doctype/membership/membership.json
@@ -10,6 +10,7 @@
   "member_name",
   "membership_type",
   "column_break_3",
+  "company",
   "membership_status",
   "membership_validity_section",
   "from_date",
@@ -132,11 +133,18 @@
    "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": "2021-01-21 16:31:20.032656",
+ "modified": "2021-02-19 14:33:44.925122",
  "modified_by": "Administrator",
  "module": "Non Profit",
  "name": "Membership",
diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py
index c113b80..52447e4 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
@@ -47,7 +48,7 @@
 		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 and last_membership.name != self.name 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"))
@@ -58,7 +59,7 @@
 		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)
@@ -68,9 +69,9 @@
 			return
 		self.load_from_db()
 		self.db_set("paid", 1)
-		settings = frappe.get_doc("Membership Settings")
-		if settings.enable_invoicing and settings.create_for_web_forms:
-			self.generate_invoice(with_payment_entry=settings.make_payment_entry, save=True)
+		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, with_payment_entry=False):
@@ -85,10 +86,11 @@
 			frappe.throw(_("No customer linked to member {0}").format(frappe.bold(self.member)))
 
 		plan = frappe.get_doc("Membership Type", self.membership_type)
-		settings = frappe.get_doc("Membership Settings")
+		settings = frappe.get_doc("Non Profit Settings")
 		self.validate_membership_type_and_settings(plan, settings)
 
 		invoice = make_invoice(self, member, plan, settings)
+		self.reload()
 		self.invoice = invoice.name
 
 		if with_payment_entry:
@@ -102,7 +104,7 @@
 	def validate_membership_type_and_settings(self, plan, settings):
 		settings_link = get_link_to_form("Membership Type", self.membership_type)
 
-		if not settings.debit_account:
+		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:
@@ -113,25 +115,26 @@
 				get_link_to_form("Membership Type", self.membership_type)))
 
 	def make_payment_entry(self, settings, invoice):
-		if not settings.payment_account:
-			frappe.throw(_("You need to set <b>Payment Account</b> in {0}").format(
-				get_link_to_form("Membership Type", self.membership_type)))
+		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.payment_account
+		pe.paid_to = settings.membership_payment_account
 		pe.reference_no = self.name
 		pe.reference_date = getdate()
-		pe.save(ignore_permissions=True)
+		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 {0}").format(
-				get_link_to_form("Membership Settings", "Membership Settings")))
+				get_link_to_form("Non Profit Settings", "Non Profit Settings")))
 
 		member = frappe.get_doc("Member", self.member)
 		if not member.email_id:
@@ -170,7 +173,7 @@
 	invoice = frappe.get_doc({
 		"doctype": "Sales Invoice",
 		"customer": member.customer,
-		"debit_to": settings.debit_account,
+		"debit_to": settings.membership_debit_account,
 		"currency": membership.currency,
 		"company": settings.company,
 		"is_pos": 0,
@@ -183,7 +186,7 @@
 		]
 	})
 	invoice.set_missing_values()
-	invoice.insert(ignore_permissions=True)
+	invoice.insert()
 	invoice.submit()
 
 	frappe.msgprint(_("Sales Invoice created successfully"))
@@ -203,17 +206,18 @@
 		return None
 
 
-def verify_signature(data):
-	if frappe.flags.in_test:
+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("Membership Settings")
-	key = settings.get_webhook_secret()
+	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)
@@ -222,7 +226,7 @@
 	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}
 
@@ -250,16 +254,15 @@
 
 			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,
@@ -270,15 +273,23 @@
 			"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.reload()
+			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)
+		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}
@@ -286,6 +297,39 @@
 	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 = """
diff --git a/erpnext/non_profit/doctype/membership/test_membership.py b/erpnext/non_profit/doctype/membership/test_membership.py
index ff7e6c4..31da792 100644
--- a/erpnext/non_profit/doctype/membership/test_membership.py
+++ b/erpnext/non_profit/doctype/membership/test_membership.py
@@ -10,33 +10,7 @@
 
 class TestMembership(unittest.TestCase):
 	def setUp(self):
-		# Get default company
-		company = frappe.get_doc("Company", erpnext.get_default_company())
-
-		# update membership settings
-		settings = frappe.get_doc("Membership Settings")
-		# Enable razorpay
-		settings.enable_razorpay = 1
-		settings.billing_cycle = "Monthly"
-		settings.billing_frequency = 24
-		# Enable invoicing
-		settings.enable_invoicing = 1
-		settings.make_payment_entry = 1
-		settings.company = company.name
-		settings.payment_account = company.default_cash_account
-		settings.debit_account = company.default_receivable_account
-		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")
+		plan = setup_membership()
 
 		# make test member
 		self.member_doc = create_member(frappe._dict({
@@ -78,7 +52,7 @@
 		})
 
 def set_config(key, value):
-	frappe.db.set_value("Membership Settings", None, key, value)
+	frappe.db.set_value("Non Profit Settings", None, key, value)
 
 def make_membership(member, payload={}):
 	data = {
@@ -109,3 +83,36 @@
 	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 c95aab2..0000000
--- a/erpnext/non_profit/doctype/membership_settings/membership_settings.js
+++ /dev/null
@@ -1,81 +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() {
-			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("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("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 3887b0a..0000000
--- a/erpnext/non_profit/doctype/membership_settings/membership_settings.json
+++ /dev/null
@@ -1,192 +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_invoicing",
-  "create_for_web_forms",
-  "make_payment_entry",
-  "company",
-  "debit_account",
-  "payment_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"
-  },
-  {
-   "depends_on": "eval:doc.enable_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_invoicing",
-   "fieldname": "company",
-   "fieldtype": "Link",
-   "label": "Company",
-   "mandatory_depends_on": "eval:doc.enable_auto_invoicing",
-   "options": "Company"
-  },
-  {
-   "default": "0",
-   "depends_on": "eval:doc.enable_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": "enable_invoicing",
-   "fieldtype": "Check",
-   "label": "Enable Invoicing",
-   "mandatory_depends_on": "eval:doc.send_invoice || doc.make_payment_entry"
-  },
-  {
-   "default": "0",
-   "depends_on": "eval:doc.enable_invoicing",
-   "description": "Auto creates Payment Entry for Sales Invoices created for Membership from web forms.",
-   "fieldname": "make_payment_entry",
-   "fieldtype": "Check",
-   "label": "Make Payment Entry"
-  },
-  {
-   "depends_on": "eval:doc.make_payment_entry",
-   "fieldname": "payment_account",
-   "fieldtype": "Link",
-   "label": "Payment To",
-   "mandatory_depends_on": "eval:doc.make_payment_entry",
-   "options": "Account"
-  },
-  {
-   "default": "0",
-   "depends_on": "eval:doc.enable_invoicing",
-   "description": "Automatically create an invoice when payment is authorized from a web form entry",
-   "fieldname": "create_for_web_forms",
-   "fieldtype": "Check",
-   "label": "Auto Create Invoice for Web Forms"
-  }
- ],
- "index_web_pages_for_search": 1,
- "issingle": 1,
- "links": [],
- "modified": "2021-01-21 19:57:53.213286",
- "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 91a5cb7..2f24276 100644
--- a/erpnext/non_profit/doctype/membership_type/membership_type.js
+++ b/erpnext/non_profit/doctype/membership_type/membership_type.js
@@ -3,11 +3,11 @@
 
 frappe.ui.form.on('Membership Type', {
 	refresh: 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.set_df_property('razorpay_plan_id', 'hidden', false);
 		});
 
-		frappe.db.get_single_value('Membership Settings', 'enable_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);
 		});
 
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%
rename from erpnext/non_profit/doctype/membership_settings/test_membership_settings.py
rename 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 80e2f1c..20ea509 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -753,3 +753,5 @@
 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/v13_0/item_reposting_for_incorrect_sl_and_gl.py b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py
index d968e1f..021bb72 100644
--- 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
@@ -20,9 +20,11 @@
 	frappe.clear_cache()
 	frappe.flags.warehouse_account_map = {}
 
+	company_list = []
+
 	data = frappe.db.sql('''
 		SELECT
-			name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time
+			name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time, company
 		FROM
 			`tabStock Ledger Entry`
 		WHERE
@@ -36,6 +38,9 @@
 	total_sle = len(data)
 	i = 0
 	for d in data:
+		if d.company not in company_list:
+			company_list.append(d.company)
+
 		update_entries_after({
 			"item_code": d.item_code,
 			"warehouse": d.warehouse,
@@ -53,8 +58,10 @@
 
 	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)
+	if data:
+		for row in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
+			if row.name in company_list:
+				update_gl_entries_after(posting_date, posting_time, company=row.name)
 
 	frappe.db.auto_commit_on_many_writes = 0
 
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/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/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py
index f5af677..029e11f 100644
--- a/erpnext/payroll/doctype/additional_salary/additional_salary.py
+++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py
@@ -89,10 +89,11 @@
 		no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1
 		return amount_per_day * no_of_days
 
-@frappe.whitelist()
-def get_additional_salary_component(employee, start_date, end_date, component_type):
-	additional_salaries = frappe.db.sql("""
-		select name, salary_component, type, amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date
+def get_additional_salaries(employee, start_date, end_date, component_type):
+	additional_salary_list = frappe.db.sql("""
+		select name, salary_component as component, type, amount,
+		overwrite_salary_structure_amount as overwrite,
+		deduct_full_tax_on_selected_payroll_date
 		from `tabAdditional Salary`
 		where employee=%(employee)s
 			and docstatus = 1
@@ -102,7 +103,7 @@
 					from_date <= %(to_date)s and to_date >= %(to_date)s
 				)
 		and type = %(component_type)s
-		order by salary_component, overwrite_salary_structure_amount DESC
+		order by salary_component, overwrite ASC
 	""", {
 		'employee': employee,
 		'from_date': start_date,
@@ -110,38 +111,18 @@
 		'component_type': "Earning" if component_type == "earnings" else "Deduction"
 	}, as_dict=1)
 
-	existing_salary_components= []
-	salary_components_details = {}
-	additional_salary_details = []
+	additional_salaries = []
+	components_to_overwrite = []
 
-	overwrites_components = [ele.salary_component for ele in additional_salaries if ele.overwrite_salary_structure_amount == 1]
+	for d in additional_salary_list:
+		if d.overwrite:
+			if d.component in components_to_overwrite:
+				frappe.throw(_("Multiple Additional Salaries with overwrite "
+					"property exist for Salary Component {0} between {1} and {2}.").format(
+					frappe.bold(d.component), start_date, end_date), title=_("Error"))
 
-	component_fields = ["depends_on_payment_days", "salary_component_abbr", "is_tax_applicable", "variable_based_on_taxable_salary", 'type']
-	for d in additional_salaries:
+			components_to_overwrite.append(d.component)
 
-		if d.salary_component not in existing_salary_components:
-			component = frappe.get_all("Salary Component", filters={'name': d.salary_component}, fields=component_fields)
-			struct_row = frappe._dict({'salary_component': d.salary_component})
-			if component:
-				struct_row.update(component[0])
+		additional_salaries.append(d)
 
-			struct_row['deduct_full_tax_on_selected_payroll_date'] = d.deduct_full_tax_on_selected_payroll_date
-			struct_row['is_additional_component'] = 1
-
-			salary_components_details[d.salary_component] = struct_row
-
-
-		if overwrites_components.count(d.salary_component) > 1:
-			frappe.throw(_("Multiple Additional Salaries with overwrite property exist for Salary Component: {0} between {1} and {2}.".format(d.salary_component, start_date, end_date)), title=_("Error"))
-		else:
-			additional_salary_details.append({
-				'name': d.name,
-				'component': d.salary_component,
-				'amount': d.amount,
-				'type': d.type,
-				'overwrite': d.overwrite_salary_structure_amount,
-			})
-
-		existing_salary_components.append(d.salary_component)
-
-	return salary_components_details, additional_salary_details
+	return additional_salaries
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index 5c5eccd..0053c0c 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -13,7 +13,7 @@
 from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
 from erpnext.utilities.transaction_base import TransactionBase
 from frappe.utils.background_jobs import enqueue
-from erpnext.payroll.doctype.additional_salary.additional_salary import get_additional_salary_component
+from erpnext.payroll.doctype.additional_salary.additional_salary import get_additional_salaries
 from erpnext.payroll.doctype.payroll_period.payroll_period import get_period_factor, get_payroll_period
 from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_benefit_component_amount
 from erpnext.payroll.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits
@@ -540,15 +540,16 @@
 						self.update_component_row(frappe._dict(last_benefit.struct_row), amount, "earnings")
 
 	def add_additional_salary_components(self, component_type):
-		salary_components_details, additional_salary_details = get_additional_salary_component(self.employee,
+		additional_salaries = get_additional_salaries(self.employee,
 			self.start_date, self.end_date, component_type)
-		if salary_components_details and additional_salary_details:
-			for additional_salary in additional_salary_details:
-				additional_salary =frappe._dict(additional_salary)
-				amount = additional_salary.amount
-				overwrite = additional_salary.overwrite
-				self.update_component_row(frappe._dict(salary_components_details[additional_salary.component]), amount,
-					component_type, overwrite=overwrite, additional_salary=additional_salary.name)
+
+		for additional_salary in additional_salaries:
+			self.update_component_row(
+				get_salary_component_data(additional_salary.component),
+				additional_salary.amount,
+				component_type,
+				additional_salary
+			)
 
 	def add_tax_components(self, payroll_period):
 		# Calculate variable_based_on_taxable_salary after all components updated in salary slip
@@ -565,46 +566,59 @@
 
 		for d in tax_components:
 			tax_amount = self.calculate_variable_based_on_taxable_salary(d, payroll_period)
-			tax_row = self.get_salary_slip_row(d)
+			tax_row = get_salary_component_data(d)
 			self.update_component_row(tax_row, tax_amount, "deductions")
 
-	def update_component_row(self, struct_row, amount, key, overwrite=1, additional_salary = ''):
+	def update_component_row(self, component_data, amount, component_type, additional_salary=None):
 		component_row = None
-		for d in self.get(key):
-			if d.salary_component == struct_row.salary_component:
+		for d in self.get(component_type):
+			if d.salary_component != component_data.salary_component:
+				continue
+
+			if (
+				(not d.additional_salary
+				and (not additional_salary or additional_salary.overwrite))
+				or (additional_salary
+				and additional_salary.name == d.additional_salary)
+			):
 				component_row = d
-		if not component_row or (struct_row.get("is_additional_component") and not overwrite):
-			if amount:
-				self.append(key, {
-					'amount': amount,
-					'default_amount': amount if not struct_row.get("is_additional_component") else 0,
-					'depends_on_payment_days' : struct_row.depends_on_payment_days,
-					'salary_component' : struct_row.salary_component,
-					'abbr' : struct_row.abbr 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,
-					'is_flexible_benefit': struct_row.is_flexible_benefit,
-					'variable_based_on_taxable_salary': struct_row.variable_based_on_taxable_salary,
-					'deduct_full_tax_on_selected_payroll_date': struct_row.deduct_full_tax_on_selected_payroll_date,
-					'additional_amount': amount if struct_row.get("is_additional_component") else 0,
-					'exempted_from_income_tax': struct_row.exempted_from_income_tax
-				})
+				break
+
+		if additional_salary and additional_salary.overwrite:
+			# Additional Salary with overwrite checked, remove default rows of same component
+			self.set(component_type, [
+				d for d in self.get(component_type)
+				if d.salary_component != component_data.salary_component
+				or (d.additional_salary and additional_salary.name != d.additional_salary)
+				or d == component_row
+			])
+
+		if not component_row:
+			if not amount:
+				return
+
+			component_row = self.append(component_type)
+			for attr in (
+				'depends_on_payment_days', 'salary_component', 'abbr'
+				'do_not_include_in_total', 'is_tax_applicable',
+				'is_flexible_benefit', 'variable_based_on_taxable_salary',
+				'exempted_from_income_tax'
+			):
+				component_row.set(attr, component_data.get(attr))
+
+		if additional_salary:
+			component_row.default_amount = 0
+			component_row.additional_amount = amount
+			component_row.additional_salary = additional_salary.name
+			component_row.deduct_full_tax_on_selected_payroll_date = \
+				additional_salary.deduct_full_tax_on_selected_payroll_date
 		else:
-			if struct_row.get("is_additional_component"):
-				if overwrite:
-					component_row.additional_amount = amount - component_row.get("default_amount", 0)
-					component_row.additional_salary = additional_salary
-				else:
-					component_row.additional_amount = amount
+			component_row.default_amount = amount
+			component_row.additional_amount = 0
+			component_row.deduct_full_tax_on_selected_payroll_date = \
+				component_data.deduct_full_tax_on_selected_payroll_date
 
-				if not overwrite and component_row.default_amount:
-					amount += component_row.default_amount
-			else:
-				component_row.default_amount = amount
-
-			component_row.amount = amount
-			component_row.deduct_full_tax_on_selected_payroll_date = struct_row.deduct_full_tax_on_selected_payroll_date
+		component_row.amount = amount
 
 	def calculate_variable_based_on_taxable_salary(self, tax_component, payroll_period):
 		if not payroll_period:
@@ -937,19 +951,6 @@
 			frappe.throw(_("Error in formula or condition: {0}").format(e))
 			raise
 
-	def get_salary_slip_row(self, salary_component):
-		component = frappe.get_doc("Salary Component", salary_component)
-		# Data for update_component_row
-		struct_row = frappe._dict()
-		struct_row['depends_on_payment_days'] = component.depends_on_payment_days
-		struct_row['salary_component'] = component.name
-		struct_row['abbr'] = component.salary_component_abbr
-		struct_row['do_not_include_in_total'] = component.do_not_include_in_total
-		struct_row['is_tax_applicable'] = component.is_tax_applicable
-		struct_row['is_flexible_benefit'] = component.is_flexible_benefit
-		struct_row['variable_based_on_taxable_salary'] = component.variable_based_on_taxable_salary
-		return struct_row
-
 	def get_component_totals(self, component_type, depends_on_payment_days=0):
 		joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
 			["date_of_joining", "relieving_date"])
@@ -1012,7 +1013,6 @@
 			self.total_loan_repayment += payment.total_payment
 
 	def get_loan_details(self):
-
 		return frappe.get_all("Loan",
 			fields=["name", "interest_income_account", "loan_account", "loan_type"],
 			filters = {
@@ -1241,4 +1241,20 @@
 
 def generate_password_for_pdf(policy_template, employee):
 	employee = frappe.get_doc("Employee", employee)
-	return policy_template.format(**employee.as_dict())
\ No newline at end of file
+	return policy_template.format(**employee.as_dict())
+
+def get_salary_component_data(component):
+	return frappe.get_value(
+		"Salary Component",
+		component,
+		[
+			"name as salary_component",
+			"depends_on_payment_days",
+			"salary_component_abbr as abbr",
+			"do_not_include_in_total",
+			"is_tax_applicable",
+			"is_flexible_benefit",
+			"variable_based_on_taxable_salary",
+		],
+		as_dict=1,
+	)
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
index f58a8e5..1402f3a 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -245,7 +245,7 @@
 		make_salary_structure("Test Loan Repayment Salary Structure", "Monthly", employee=applicant, currency='INR',
 			payroll_period=payroll_period)
 
-		frappe.db.sql("""delete from `tabLoan""")
+		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
 		loan.submit()
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 123d998..e0b0b27 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -576,7 +576,7 @@
 									var d = locals[cdt][cdn];
 									me.add_taxes_from_item_tax_template(d.item_tax_rate);
 									if (d.free_item_data) {
-										me.apply_product_discount(d.free_item_data);
+										me.apply_product_discount(d);
 									}
 								},
 								() => {
@@ -703,21 +703,15 @@
 			}
 			else {
 				var valid_serial_nos = [];
-
+				var serialnos = [];
 				// Replacing all occurences of comma with carriage return
-				var serial_nos = item.serial_no.trim().replace(/,/g, '\n');
-
-				serial_nos = serial_nos.trim().split('\n');
-
-				// Trim each string and push unique string to new list
-				for (var x=0; x<=serial_nos.length - 1; x++) {
-					if (serial_nos[x].trim() != "" && valid_serial_nos.indexOf(serial_nos[x].trim()) == -1) {
-						valid_serial_nos.push(serial_nos[x].trim());
+				item.serial_no = item.serial_no.replace(/,/g, '\n');
+				serialnos = item.serial_no.split("\n");
+				for (var i = 0; i < serialnos.length; i++) {
+					if (serialnos[i] != "") {
+						valid_serial_nos.push(serialnos[i]);
 					}
 				}
-
-				// Add the new list to the serial no. field in grid with each in new line
-				item.serial_no = valid_serial_nos.join('\n');
 				item.conversion_factor = item.conversion_factor || 1;
 
 				refresh_field("serial_no", item.name, item.parentfield);
@@ -1138,6 +1132,11 @@
 				this.calculate_net_weight();
 			}
 
+			// for handling customization not to fetch price list rate
+			if(frappe.flags.dont_fetch_price_list_rate) {
+				return
+			}
+			
 			if (!dont_fetch_price_list_rate &&
 				frappe.meta.has_field(doc.doctype, "price_list_currency")) {
 				this.apply_price_list(item, true);
@@ -1169,7 +1168,7 @@
 
 	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);	
+		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) {
@@ -1498,7 +1497,10 @@
 					if(k=="price_list_rate") {
 						if(flt(v) != flt(d.price_list_rate)) price_list_rate_changed = true;
 					}
-					frappe.model.set_value(d.doctype, d.name, k, v);
+
+					if (k !== 'free_item_data') {
+						frappe.model.set_value(d.doctype, d.name, k, v);
+					}
 				}
 			}
 
@@ -1510,7 +1512,7 @@
 			}
 
 			if (d.free_item_data) {
-				me.apply_product_discount(d.free_item_data);
+				me.apply_product_discount(d);
 			}
 
 			if (d.apply_rule_on_other_items) {
@@ -1544,20 +1546,31 @@
 		}
 	},
 
-	apply_product_discount: function(free_item_data) {
-		const items = this.frm.doc.items.filter(d => (d.item_code == free_item_data.item_code
-			&& d.is_free_item)) || [];
+	apply_product_discount: function(args) {
+		const items = this.frm.doc.items.filter(d => (d.is_free_item)) || [];
 
-		if (!items.length) {
-			let row_to_modify = frappe.model.add_child(this.frm.doc,
-				this.frm.doc.doctype + ' Item', 'items');
+		const exist_items = items.map(row => (row.item_code, row.pricing_rules));
 
-			for (let key in free_item_data) {
-				row_to_modify[key] = free_item_data[key];
+		args.free_item_data.forEach(pr_row => {
+			let row_to_modify = {};
+			if (!items || !in_list(exist_items, (pr_row.item_code, pr_row.pricing_rules))) {
+
+				row_to_modify = frappe.model.add_child(this.frm.doc,
+					this.frm.doc.doctype + ' Item', 'items');
+
+			} else if(items) {
+				row_to_modify = items.filter(d => (d.item_code === pr_row.item_code
+					&& d.pricing_rules === pr_row.pricing_rules))[0];
 			}
-		} if (items && items.length && free_item_data) {
-			items[0].qty = free_item_data.qty
-		}
+
+			for (let key in pr_row) {
+				row_to_modify[key] = pr_row[key];
+			}
+		});
+
+		// free_item_data is a temporary variable
+		args.free_item_data = '';
+		refresh_field('items');
 	},
 
 	apply_price_list: function(item, reset_plc_conversion) {
@@ -1884,7 +1897,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}
 			}
@@ -1910,9 +1922,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],
@@ -2123,4 +2134,4 @@
 			}
 		}
 	});
-};
\ No newline at end of file
+};
diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
index 68c8a0d..a49996d 100644
--- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
+++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
@@ -349,13 +349,12 @@
 		return inter_state_supply_details
 
 	def get_inward_nil_exempt(self, state):
-
 		inward_nil_exempt = frappe.db.sql(""" select p.place_of_supply, sum(i.base_amount) as base_amount,
 			i.is_nil_exempt, i.is_non_gst from `tabPurchase Invoice` p , `tabPurchase Invoice Item` i
 			where p.docstatus = 1 and p.name = i.parent
-			and i.is_nil_exempt = 1 or i.is_non_gst = 1 and
+			and (i.is_nil_exempt = 1 or i.is_non_gst = 1) and
 			month(p.posting_date) = %s and year(p.posting_date) = %s and p.company = %s and p.company_gstin = %s
-			group by p.place_of_supply """, (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1)
+			group by p.place_of_supply, i.is_nil_exempt, i.is_non_gst""", (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1)
 
 		inward_nil_exempt_details = {
 			"gst": {
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..5bbd575
--- /dev/null
+++ b/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.py
@@ -0,0 +1,102 @@
+# -*- 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.calculate_total()
+		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,
+				'name': ('!=', self.name)
+			})
+			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 calculate_total(self):
+		if self.recipient == 'Donor':
+			return
+
+		total = 0
+		for entry in self.payments:
+			total += flt(entry.amount)
+		self.total = total
+
+	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/india/e_invoice/einvoice.js b/erpnext/regional/india/e_invoice/einvoice.js
index e8a7c30..cad2acd 100644
--- a/erpnext/regional/india/e_invoice/einvoice.js
+++ b/erpnext/regional/india/e_invoice/einvoice.js
@@ -1,7 +1,8 @@
 erpnext.setup_einvoice_actions = (doctype) => {
 	frappe.ui.form.on(doctype, {
-		refresh(frm) {
-			const einvoicing_enabled = frappe.db.get_value("E Invoice Settings", "E Invoice Settings", "enable");
+		async refresh(frm) {
+			const { message } = await frappe.db.get_value("E Invoice Settings", "E Invoice Settings", "enable");
+			const einvoicing_enabled = cint(message.enable);
 			const supply_type = frm.doc.gst_category;
 			const valid_supply_type = ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export'].includes(supply_type);
 			const company_transaction = frm.doc.billing_address_gstin == frm.doc.company_gstin;
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index 5261984..40247f7 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -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',
@@ -642,7 +658,7 @@
 		pass
 
 	docs = get_tds_details(accounts, fiscal_year)
-	
+
 	for d in docs:
 		try:
 			doc = frappe.get_doc(d)
@@ -660,7 +676,7 @@
 				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()
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index d6200c9..3757f63 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -693,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
@@ -745,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':
@@ -775,3 +764,42 @@
 				)
 
 	return gl_entries
+
+def get_gst_tax_amount(doc):
+	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
+
+	return gst_tax, base_gst_tax
+
+@frappe.whitelist()
+def get_regional_round_off_accounts(company, account_list):
+	country = frappe.get_cached_value('Company', company, 'country')
+
+	if country != 'India':
+		return
+
+	if isinstance(account_list, string_types):
+		account_list = json.loads(account_list)
+
+	if not frappe.db.get_single_value('GST Settings', 'round_off_gst_values'):
+		return
+
+	gst_accounts = get_gst_accounts(company)
+	gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \
+		+ gst_accounts.get('igst_account')
+
+	account_list.extend(gst_account_list)
+
+	return account_list
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/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index 45b4e30..89fd9c7 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -106,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);
diff --git a/erpnext/setup/doctype/naming_series/naming_series.py b/erpnext/setup/doctype/naming_series/naming_series.py
index abff973..2ea0bc0 100644
--- a/erpnext/setup/doctype/naming_series/naming_series.py
+++ b/erpnext/setup/doctype/naming_series/naming_series.py
@@ -10,6 +10,7 @@
 from frappe.model.document import Document
 from frappe.model.naming import parse_naming_series
 from frappe.permissions import get_doctypes_with_read
+from frappe.core.doctype.doctype.doctype import validate_series
 
 class NamingSeriesNotSetError(frappe.ValidationError): pass
 
@@ -126,7 +127,7 @@
 		dt = frappe.get_doc("DocType", self.select_doc_for_series)
 		options = self.scrub_options_list(self.set_options.split("\n"))
 		for series in options:
-			dt.validate_series(series)
+			validate_series(dt, series)
 			for i in sr:
 				if i[0]:
 					existing_series = [d.split('.')[0] for d in i[0].split("\n")]
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/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/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index 8436acb..a75db1a 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -5,7 +5,7 @@
 from __future__ import unicode_literals
 import frappe, erpnext
 from frappe.model.document import Document
-from frappe.utils import cint, get_link_to_form
+from frappe.utils import cint, get_link_to_form, add_to_date, today
 from erpnext.stock.stock_ledger import repost_future_sle
 from erpnext.accounts.utils import update_gl_entries_after, check_if_stock_and_account_balance_synced
 from frappe.utils.user import get_users_with_role
@@ -29,7 +29,7 @@
 			self.company = frappe.get_cached_value(self.voucher_type, self.voucher_no, "company")
 		elif self.warehouse:
 			self.company = frappe.get_cached_value("Warehouse", self.warehouse, "company")
-	
+
 	def set_status(self, status=None):
 		if not status:
 			status = 'Queued'
@@ -54,7 +54,6 @@
 
 		repost_sl_entries(doc)
 		repost_gl_entries(doc)
-		check_if_stock_and_account_balance_synced(doc.posting_date, doc.company)
 
 		doc.set_status('Completed')
 	except Exception:
@@ -103,7 +102,7 @@
 	recipients = get_users_with_role("Stock Manager")
 	if not recipients:
 		get_users_with_role("System Manager")
-	
+
 	subject = _("Error while reposting item valuation")
 	message = (_("Hi,") + "<br>"
 		+ _("An error has been appeared while reposting item valuation via {0}")
@@ -112,4 +111,24 @@
 	)
 	frappe.sendmail(recipients=recipients, subject=subject, message=message)
 
+def repost_entries():
+	riv_entries = get_repost_item_valuation_entries()
 
+	for row in riv_entries:
+		doc = frappe.get_cached_doc('Repost Item Valuation', row.name)
+		repost(doc)
+
+	riv_entries = get_repost_item_valuation_entries()
+	if riv_entries:
+		return
+
+	for d in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
+		check_if_stock_and_account_balance_synced(today(), d.name)
+
+def get_repost_item_valuation_entries():
+	date = add_to_date(today(), hours=-12)
+
+	return frappe.db.sql(""" SELECT name from `tabRepost Item Valuation`
+		WHERE status != 'Completed' and creation <= %s and docstatus = 1
+		ORDER BY timestamp(posting_date, posting_time) asc, creation asc
+	""", date, as_dict=1)
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 274bbc2..4d1a71b 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -817,7 +817,6 @@
 		}
 		erpnext.hide_company();
 		erpnext.utils.add_item(this.frm);
-		this.frm.trigger('add_to_transit');
 	},
 
 	scan_barcode: function() {
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index d77b70f..9cdc3cf 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -163,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))
 
@@ -823,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)
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 988ae92..864ff48 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
@@ -69,7 +69,8 @@
   "putaway_rule",
   "column_break_51",
   "reference_purchase_receipt",
-  "quality_inspection"
+  "quality_inspection",
+  "job_card_item"
  ],
  "fields": [
   {
@@ -532,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-30 15:00:44.489442",
+ "modified": "2021-02-11 13:47:50.158754",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry Detail",
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index f0a90f9..b452e96 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -29,6 +29,8 @@
 		self.remove_items_with_no_change()
 		self.validate_data()
 		self.validate_expense_account()
+		self.validate_customer_provided_item()
+		self.set_zero_value_for_customer_provided_items()
 		self.set_total_qty_and_amount()
 		self.validate_putaway_capacity()
 
@@ -217,7 +219,7 @@
 					if row.valuation_rate in ("", None):
 						row.valuation_rate = previous_sle.get("valuation_rate", 0)
 
-				if row.qty and not row.valuation_rate:
+				if row.qty and not row.valuation_rate and not row.allow_zero_valuation_rate:
 					frappe.throw(_("Valuation Rate required for Item {0} at row {1}").format(row.item_code, row.idx))
 
 				if ((previous_sle and row.qty == previous_sle.get("qty_after_transaction")
@@ -436,6 +438,20 @@
 			if frappe.db.get_value("Account", self.expense_account, "report_type") == "Profit and Loss":
 				frappe.throw(_("Difference Account must be a Asset/Liability type account, since this Stock Reconciliation is an Opening Entry"), OpeningEntryAccountError)
 
+	def set_zero_value_for_customer_provided_items(self):
+		changed_any_values = False
+
+		for d in self.get('items'):
+			is_customer_item = frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item')
+			if is_customer_item and d.valuation_rate:
+				d.valuation_rate = 0.0
+				changed_any_values = True
+
+		if changed_any_values:
+			msgprint(_("Valuation rate for customer provided items has been set to zero."),
+				title=_("Note"), indicator="blue")
+
+
 	def set_total_qty_and_amount(self):
 		for d in self.get("items"):
 			d.amount = flt(d.qty, d.precision("qty")) * flt(d.valuation_rate, d.precision("valuation_rate"))
@@ -531,4 +547,4 @@
 		account = frappe.db.get_value('Account', {'is_group': 0,
 			'company': company, 'account_type': 'Temporary'}, 'name')
 
-	return account
\ No newline at end of file
+	return account
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 088456f..6690c6a 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -193,6 +193,16 @@
 			stock_doc = frappe.get_doc("Stock Reconciliation", d)
 			stock_doc.cancel()
 
+	def test_customer_provided_items(self):
+		item_code = 'Stock-Reco-customer-Item-100'
+		create_item(item_code, is_customer_provided_item = 1,
+			  customer = '_Test Customer', is_purchase_item = 0)
+
+		sr = create_stock_reconciliation(item_code = item_code, qty = 10, rate = 420)
+
+		self.assertEqual(sr.get("items")[0].allow_zero_valuation_rate, 1)
+		self.assertEqual(sr.get("items")[0].valuation_rate, 0)
+		self.assertEqual(sr.get("items")[0].amount, 0)
 
 def insert_existing_sle(warehouse):
 	from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
index e53db07..85c7ebe 100644
--- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
+++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
@@ -13,6 +13,7 @@
   "qty",
   "valuation_rate",
   "amount",
+  "allow_zero_valuation_rate",
   "serial_no_and_batch_section",
   "serial_no",
   "column_break_11",
@@ -166,10 +167,19 @@
    "fieldtype": "Link",
    "label": "Batch No",
    "options": "Batch"
+  },
+  {
+   "default": "0",
+   "fieldname": "allow_zero_valuation_rate",
+   "fieldtype": "Check",
+   "label": "Allow Zero Valuation Rate",
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "istable": 1,
- "modified": "2019-06-14 17:10:53.188305",
+ "links": [],
+ "modified": "2021-03-23 11:09:44.407157",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Reconciliation Item",
@@ -179,4 +189,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 873cfec..70e4c2c 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -314,7 +314,9 @@
 		"last_purchase_rate": item.last_purchase_rate if args.get("doctype") in ["Purchase Order"] else 0,
 		"transaction_date": args.get("transaction_date"),
 		"against_blanket_order": args.get("against_blanket_order"),
-		"bom_no": item.get("default_bom")
+		"bom_no": item.get("default_bom"),
+		"weight_per_unit": args.get("weight_per_unit") or item.get("weight_per_unit"),
+		"weight_uom": args.get("weight_uom") or item.get("weight_uom")
 	})
 
 	if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"):
@@ -369,6 +371,9 @@
 	if meta.get_field("barcode"):
 		update_barcode_value(out)
 
+	if out.get("weight_per_unit"):
+		out['total_weight'] = out.weight_per_unit * out.stock_qty
+
 	return out
 
 def get_item_warehouse(item, args, overwrite_warehouse, defaults={}):
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index e5d4d62..6dfede4 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -198,7 +198,7 @@
 		else:
 			qty_diff = flt(d.actual_qty)
 
-		value_diff = flt(d.stock_value) - flt(qty_dict.bal_val)
+		value_diff = flt(d.stock_value_difference)
 
 		if d.posting_date < from_date:
 			qty_dict.opening_qty += qty_diff
diff --git a/erpnext/support/report/issue_summary/issue_summary.py b/erpnext/support/report/issue_summary/issue_summary.py
index 3d73531..7861e30 100644
--- a/erpnext/support/report/issue_summary/issue_summary.py
+++ b/erpnext/support/report/issue_summary/issue_summary.py
@@ -260,8 +260,7 @@
 					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
+		self.chart = []
 
 		labels = []
 		open_issues = []
@@ -310,8 +309,7 @@
 		}
 
 	def get_report_summary(self):
-		if not self.data:
-			return None
+		self.report_summary = []
 
 		open_issues = 0
 		replied = 0