feat(Non Profit): 80G Certificates and Donations  (#24657)

* feat: 80G Certificates

* feat: add non-profit custom fields for India

* feat: 80G Certificate print format for memberships

* feat: Donation doctype and API endpoint to capture donations via razorpay

* chore: Rename Membership Settings to Non Profit Settings

* chore: clean up Non Profit Settings

- Rename fields, better labels

- patch for renaming

* feat: Webhook secret generation for Razorpay donations in Non-Profit Settings

* feat: Payment Entry for donations

- added Donor as Party Type

- setting for automating payment entries for donations created via web form

* fix: linter and sider issues

* fix: translation syntax

* feat: PAN Details custom field for Indian donors

* feat: 80G certificates for Donations with Print Format

* fix: sider

* feat: validations for donor and donation

- create donor for website user from donations

- validate donor email

* feat: extract member name from subscription notes

* test: Donation

* test: Tax Exemption 80G Certificate

* chore: styling fixes

* fix: tests

* fix: sider

* feat: extract PAN number from additional subscription notes

* feat: Add creation user field in Non Profit Settings

fix: Payment Entry not generating for memberships and donations

* feat: update desk page

* fix: tests
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index f5c488d..6412772 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() {
@@ -705,7 +707,8 @@
 						(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") ||
 						(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier")  ||
 						(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Employee") ||
-						(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student")
+						(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student") ||
+						(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Donor")
 					) {
 						if(total_positive_outstanding > total_negative_outstanding)
 							if (!frm.doc.paid_amount)
@@ -748,7 +751,8 @@
 				(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") ||
 				(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier") ||
 				(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Employee") ||
-				(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student")
+				(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student") ||
+				(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Donor")
 			) {
 				if(total_positive_outstanding_including_order > paid_amount) {
 					var remaining_outstanding = total_positive_outstanding_including_order - paid_amount;
@@ -905,6 +909,12 @@
 				frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Expense Claim or Journal Entry", [row.idx]));
 				return false;
 			}
+
+			if (frm.doc.party_type == "Donor" && row.reference_doctype != "Donation") {
+				frappe.model.set_value(row.doctype, row.name, "reference_doctype", null);
+				frappe.msgprint(__("Row #{0}: Reference Document Type must be Donation", [row.idx]));
+				return false;
+			}
 		}
 
 		if (row) {
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 61858b3..97a89b6 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:
@@ -618,6 +622,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()
@@ -917,6 +928,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
@@ -1166,8 +1180,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):
@@ -1193,7 +1209,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:
@@ -1226,6 +1242,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/non_profit/doctype/membership_settings/__init__.py b/erpnext/non_profit/doctype/donation/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/non_profit/doctype/donation/__init__.py
diff --git a/erpnext/non_profit/doctype/donation/donation.js b/erpnext/non_profit/doctype/donation/donation.js
new file mode 100644
index 0000000..10e8220
--- /dev/null
+++ b/erpnext/non_profit/doctype/donation/donation.js
@@ -0,0 +1,26 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Donation', {
+	refresh: function(frm) {
+		if (frm.doc.docstatus === 1 && !frm.doc.paid) {
+			frm.add_custom_button(__('Create Payment Entry'), function() {
+				frm.events.make_payment_entry(frm);
+			});
+		}
+	},
+
+	make_payment_entry: function(frm) {
+		return frappe.call({
+			method: 'erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry',
+			args: {
+				'dt': frm.doc.doctype,
+				'dn': frm.doc.name
+			},
+			callback: function(r) {
+				var doc = frappe.model.sync(r.message);
+				frappe.set_route('Form', doc[0].doctype, doc[0].name);
+			}
+		});
+	},
+});
diff --git a/erpnext/non_profit/doctype/donation/donation.json b/erpnext/non_profit/doctype/donation/donation.json
new file mode 100644
index 0000000..6759569
--- /dev/null
+++ b/erpnext/non_profit/doctype/donation/donation.json
@@ -0,0 +1,156 @@
+{
+ "actions": [],
+ "autoname": "naming_series:",
+ "creation": "2021-02-17 10:28:52.645731",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "naming_series",
+  "donor",
+  "donor_name",
+  "email",
+  "column_break_4",
+  "company",
+  "date",
+  "payment_details_section",
+  "paid",
+  "amount",
+  "mode_of_payment",
+  "razorpay_payment_id",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "fieldname": "donor",
+   "fieldtype": "Link",
+   "label": "Donor",
+   "options": "Donor",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "donor.donor_name",
+   "fieldname": "donor_name",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Donor Name",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "donor.email",
+   "fieldname": "email",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Email",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "date",
+   "fieldtype": "Date",
+   "label": "Date",
+   "reqd": 1
+  },
+  {
+   "fieldname": "payment_details_section",
+   "fieldtype": "Section Break",
+   "label": "Payment Details"
+  },
+  {
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "label": "Amount",
+   "reqd": 1
+  },
+  {
+   "fieldname": "mode_of_payment",
+   "fieldtype": "Link",
+   "label": "Mode of Payment",
+   "options": "Mode of Payment"
+  },
+  {
+   "fieldname": "razorpay_payment_id",
+   "fieldtype": "Data",
+   "label": "Razorpay Payment ID",
+   "read_only": 1
+  },
+  {
+   "fieldname": "naming_series",
+   "fieldtype": "Select",
+   "label": "Naming Series",
+   "options": "NPO-DTN-.YYYY.-"
+  },
+  {
+   "default": "0",
+   "fieldname": "paid",
+   "fieldtype": "Check",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Paid"
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Donation",
+   "print_hide": 1,
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2021-03-11 10:53:11.269005",
+ "modified_by": "Administrator",
+ "module": "Non Profit",
+ "name": "Donation",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "select": 1,
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Non Profit Manager",
+   "select": 1,
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  }
+ ],
+ "search_fields": "donor_name, email",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "donor_name",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/donation/donation.py b/erpnext/non_profit/doctype/donation/donation.py
new file mode 100644
index 0000000..e947588
--- /dev/null
+++ b/erpnext/non_profit/doctype/donation/donation.py
@@ -0,0 +1,215 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+import six
+import json
+from frappe.model.document import Document
+from frappe import _
+from frappe.utils import getdate, flt, get_link_to_form
+from frappe.email import sendmail_to_system_managers
+from erpnext.non_profit.doctype.membership.membership import verify_signature
+
+class Donation(Document):
+	def validate(self):
+		if not self.donor or not frappe.db.exists('Donor', self.donor):
+			# for web forms
+			user_type = frappe.db.get_value('User', frappe.session.user, 'user_type')
+			if user_type == 'Website User':
+				self.create_donor_for_website_user()
+			else:
+				frappe.throw(_('Please select a Member'))
+
+	def create_donor_for_website_user(self):
+		donor_name = frappe.get_value('Donor', dict(email=frappe.session.user))
+
+		if not donor_name:
+			user = frappe.get_doc('User', frappe.session.user)
+			donor = frappe.get_doc(dict(
+				doctype='Donor',
+				donor_type=self.get('donor_type'),
+				email=frappe.session.user,
+				member_name=user.get_fullname()
+			)).insert(ignore_permissions=True)
+			donor_name = donor.name
+
+		if self.get('__islocal'):
+			self.donor = donor_name
+
+	def on_payment_authorized(self, *args, **kwargs):
+		self.load_from_db()
+		self.create_payment_entry()
+
+	def create_payment_entry(self):
+		settings = frappe.get_doc('Non Profit Settings')
+		if not settings.automate_donation_payment_entries:
+			return
+
+		if not settings.donation_payment_account:
+			frappe.throw(_('You need to set <b>Payment Account</b> for Donation in {0}').format(
+				get_link_to_form('Non Profit Settings', 'Non Profit Settings')))
+
+		from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
+
+		frappe.flags.ignore_account_permission = True
+		pe = get_payment_entry(dt=self.doctype, dn=self.name)
+		frappe.flags.ignore_account_permission = False
+		pe.paid_from = settings.donation_debit_account
+		pe.paid_to = settings.donation_payment_account
+		pe.reference_no = self.name
+		pe.reference_date = getdate()
+		pe.flags.ignore_mandatory = True
+		pe.insert()
+		pe.submit()
+
+
+@frappe.whitelist(allow_guest=True)
+def capture_razorpay_donations(*args, **kwargs):
+	"""
+		Creates Donation from Razorpay Webhook Request Data on payment.captured event
+		Creates Donor from email if not found
+	"""
+	data = frappe.request.get_data(as_text=True)
+
+	try:
+		verify_signature(data, endpoint='Donation')
+	except Exception as e:
+		log = frappe.log_error(e, 'Donation Webhook Verification Error')
+		notify_failure(log)
+		return { 'status': 'Failed', 'reason': e }
+
+	if isinstance(data, six.string_types):
+		data = json.loads(data)
+	data = frappe._dict(data)
+
+	payment = data.payload.get('payment', {}).get('entity', {})
+	payment = frappe._dict(payment)
+
+	try:
+		if not data.event == 'payment.captured':
+			return
+
+		donor = get_donor(payment.email)
+		if not donor:
+			donor = create_donor(payment)
+
+		donation = create_donation(donor, payment)
+		donation.run_method('create_payment_entry')
+
+	except Exception as e:
+		message = '{0}\n\n{1}\n\n{2}: {3}'.format(e, frappe.get_traceback(), _('Payment ID'), payment.id)
+		log = frappe.log_error(message, _('Error creating donation entry for {0}').format(donor.name))
+		notify_failure(log)
+		return { 'status': 'Failed', 'reason': e }
+
+	return { 'status': 'Success' }
+
+
+def create_donation(donor, payment):
+	if not frappe.db.exists('Mode of Payment', payment.method):
+		create_mode_of_payment(payment.method)
+
+	company = get_company_for_donations()
+	donation = frappe.get_doc({
+		'doctype': 'Donation',
+		'company': company,
+		'donor': donor.name,
+		'donor_name': donor.donor_name,
+		'email': donor.email,
+		'date': getdate(),
+		'amount': flt(payment.amount),
+		'mode_of_payment': payment.method,
+		'razorpay_payment_id': payment.id
+	}).insert(ignore_mandatory=True)
+
+	donation.submit()
+	return donation
+
+
+def get_donor(email):
+	donors = frappe.get_all('Donor',
+		filters={'email': email},
+		order_by='creation desc')
+
+	try:
+		return frappe.get_doc('Donor', donors[0]['name'])
+	except Exception:
+		return None
+
+
+@frappe.whitelist()
+def create_donor(payment):
+	donor_details = frappe._dict(payment)
+	donor_type = frappe.db.get_single_value('Non Profit Settings', 'default_donor_type')
+
+	donor = frappe.new_doc('Donor')
+	donor.update({
+		'donor_name': donor_details.email,
+		'donor_type': donor_type,
+		'email': donor_details.email,
+		'contact': donor_details.contact
+	})
+
+	if donor_details.get('notes'):
+		donor = get_additional_notes(donor, donor_details)
+
+	donor.insert(ignore_mandatory=True)
+	return donor
+
+
+def get_company_for_donations():
+	company = frappe.db.get_single_value('Non Profit Settings', 'donation_company')
+	if not company:
+		from erpnext.healthcare.setup import get_company
+		company = get_company()
+	return company
+
+
+def get_additional_notes(donor, donor_details):
+	if type(donor_details.notes) == dict:
+		for k, v in donor_details.notes.items():
+			notes = '\n'.join('{}: {}'.format(k, v))
+
+			# extract donor name from notes
+			if 'name' in k.lower():
+				donor.update({
+					'donor_name': donor_details.notes.get(k)
+				})
+
+			# extract pan from notes
+			if 'pan' in k.lower():
+				donor.update({
+					'pan_number': donor_details.notes.get(k)
+				})
+
+		donor.add_comment('Comment', notes)
+
+	elif type(donor_details.notes) == str:
+		donor.add_comment('Comment', donor_details.notes)
+
+	return donor
+
+
+def create_mode_of_payment(method):
+	frappe.get_doc({
+		'doctype': 'Mode of Payment',
+		'mode_of_payment': method
+	}).insert(ignore_mandatory=True)
+
+
+def notify_failure(log):
+	try:
+		content = '''
+			Dear System Manager,
+			Razorpay webhook for creating donation failed due to some reason.
+			Please check the error log linked below
+			Error Log: {0}
+			Regards, Administrator
+		'''.format(get_link_to_form('Error Log', log.name))
+
+		sendmail_to_system_managers(_('[Important] [ERPNext] Razorpay donation webhook failed, please check.'), content)
+	except Exception:
+		pass
+
diff --git a/erpnext/non_profit/doctype/donation/donation_dashboard.py b/erpnext/non_profit/doctype/donation/donation_dashboard.py
new file mode 100644
index 0000000..7e25c8d
--- /dev/null
+++ b/erpnext/non_profit/doctype/donation/donation_dashboard.py
@@ -0,0 +1,16 @@
+from __future__ import unicode_literals
+from frappe import _
+
+def get_data():
+	return {
+		'fieldname': 'donation',
+		'non_standard_fieldnames': {
+			'Payment Entry': 'reference_name'
+		},
+		'transactions': [
+			{
+				'label': _('Payment'),
+				'items': ['Payment Entry']
+			}
+		]
+	}
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/donation/test_donation.py b/erpnext/non_profit/doctype/donation/test_donation.py
new file mode 100644
index 0000000..c6a534d
--- /dev/null
+++ b/erpnext/non_profit/doctype/donation/test_donation.py
@@ -0,0 +1,76 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+from erpnext.non_profit.doctype.donation.donation import create_donation
+
+class TestDonation(unittest.TestCase):
+	def setUp(self):
+		create_donor_type()
+		settings = frappe.get_doc('Non Profit Settings')
+		settings.company = '_Test Company'
+		settings.donation_company = '_Test Company'
+		settings.default_donor_type = '_Test Donor'
+		settings.automate_donation_payment_entries = 1
+		settings.donation_debit_account = 'Debtors - _TC'
+		settings.donation_payment_account =  'Cash - _TC'
+		settings.creation_user = 'Administrator'
+		settings.flags.ignore_permissions = True
+		settings.save()
+
+	def test_payment_entry_for_donations(self):
+		donor = create_donor()
+		create_mode_of_payment()
+		payment = frappe._dict({
+			'amount': 100,
+			'method': 'Debit Card',
+			'id': 'pay_MeXAmsgeKOhq7O'
+		})
+		donation = create_donation(donor, payment)
+
+		self.assertTrue(donation.name)
+
+		# Naive test to check if at all payment entry is generated
+		# This method is actually triggered from Payment Gateway
+		# In any case if details were missing, this would throw an error
+		donation.on_payment_authorized()
+		donation.reload()
+
+		self.assertEquals(donation.paid, 1)
+		self.assertTrue(frappe.db.exists('Payment Entry', {'reference_no': donation.name}))
+
+
+def create_donor_type():
+	if not frappe.db.exists('Donor Type', '_Test Donor'):
+		frappe.get_doc({
+			'doctype': 'Donor Type',
+			'donor_type': '_Test Donor'
+		}).insert()
+
+
+def create_donor():
+	donor = frappe.db.exists('Donor', 'donor@test.com')
+	if donor:
+		return frappe.get_doc('Donor', 'donor@test.com')
+	else:
+		return frappe.get_doc({
+			'doctype': 'Donor',
+			'donor_name': '_Test Donor',
+			'donor_type': '_Test Donor',
+			'email': 'donor@test.com'
+		}).insert()
+
+
+def create_mode_of_payment():
+	if not frappe.db.exists('Mode of Payment', 'Debit Card'):
+		frappe.get_doc({
+			'doctype': 'Mode of Payment',
+			'mode_of_payment': 'Debit Card',
+			'accounts': [{
+				'company': '_Test Company',
+				'default_account': 'Cash - _TC'
+			}]
+		}).insert()
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/donor/donor.json b/erpnext/non_profit/doctype/donor/donor.json
index 9639265..72f24ef 100644
--- a/erpnext/non_profit/doctype/donor/donor.json
+++ b/erpnext/non_profit/doctype/donor/donor.json
@@ -76,8 +76,13 @@
   }
  ],
  "image_field": "image",
- "links": [],
- "modified": "2020-09-16 23:46:04.083274",
+ "links": [
+  {
+   "link_doctype": "Donation",
+   "link_fieldname": "donor"
+  }
+ ],
+ "modified": "2021-02-17 16:36:33.470731",
  "modified_by": "Administrator",
  "module": "Non Profit",
  "name": "Donor",
diff --git a/erpnext/non_profit/doctype/donor/donor.py b/erpnext/non_profit/doctype/donor/donor.py
index 9121d0c..fb70e59 100644
--- a/erpnext/non_profit/doctype/donor/donor.py
+++ b/erpnext/non_profit/doctype/donor/donor.py
@@ -11,3 +11,8 @@
 		"""Load address and contacts in `__onload`"""
 		load_address_and_contact(self)
 
+	def validate(self):
+		from frappe.utils import validate_email_address
+		if self.email:
+			validate_email_address(self.email.strip(), True)
+
diff --git a/erpnext/non_profit/doctype/member/member.js b/erpnext/non_profit/doctype/member/member.js
index 199dcfc..6b8f1b1 100644
--- a/erpnext/non_profit/doctype/member/member.js
+++ b/erpnext/non_profit/doctype/member/member.js
@@ -3,7 +3,7 @@
 
 frappe.ui.form.on('Member', {
 	setup: function(frm) {
-		frappe.db.get_single_value("Membership Settings", "enable_razorpay").then(val => {
+		frappe.db.get_single_value('Non Profit Settings', 'enable_razorpay_for_memberships').then(val => {
 			if (val && (frm.doc.subscription_id || frm.doc.customer_id)) {
 				frm.set_df_property('razorpay_details_section', 'hidden', false);
 			}
diff --git a/erpnext/non_profit/doctype/member/member.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..57f787f 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
@@ -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,7 +86,7 @@
 			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)
@@ -102,7 +103,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 +114,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 +172,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 +185,7 @@
 		]
 	})
 	invoice.set_missing_values()
-	invoice.insert(ignore_permissions=True)
+	invoice.insert()
 	invoice.submit()
 
 	frappe.msgprint(_("Sales Invoice created successfully"))
@@ -203,17 +205,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 +225,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 +253,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,13 +272,19 @@
 			"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()
+
+		automate_payment = frappe.db.get_single_value("Membership Settings", "automate_membership_payment_entries")
+		membership.generate_invoice(with_payment_entry=automate_payment, save=True)
+
 	except Exception as e:
 		message = "{0}\n\n{1}\n\n{2}: {3}".format(e, frappe.get_traceback(), __("Payment ID"), payment.id)
 		log = frappe.log_error(message, _("Error creating membership entry for {0}").format(member.name))
@@ -286,6 +294,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..95765fd
--- /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):
+		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
index da2a514..2557d77 100644
--- a/erpnext/non_profit/workspace/non_profit/non_profit.json
+++ b/erpnext/non_profit/workspace/non_profit/non_profit.json
@@ -10,6 +10,7 @@
  "hide_custom": 0,
  "icon": "non-profit",
  "idx": 0,
+ "is_default": 0,
  "is_standard": 1,
  "label": "Non Profit",
  "links": [
@@ -109,7 +110,7 @@
    "hidden": 0,
    "is_query_report": 0,
    "label": "Membership Settings",
-   "link_to": "Membership Settings",
+   "link_to": "Non Profit Settings",
    "link_type": "DocType",
    "onboard": 0,
    "type": "Link"
@@ -161,7 +162,7 @@
   {
    "hidden": 0,
    "is_query_report": 0,
-   "label": "Donor",
+   "label": "Donation",
    "onboard": 0,
    "type": "Card Break"
   },
@@ -184,9 +185,35 @@
    "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": "2020-12-01 13:38:38.351409",
+ "modified": "2021-03-11 11:38:09.140655",
  "modified_by": "Administrator",
  "module": "Non Profit",
  "name": "Non Profit",
@@ -201,8 +228,8 @@
    "type": "DocType"
   },
   {
-   "label": "Membership Settings",
-   "link_to": "Membership Settings",
+   "label": "Non Profit Settings",
+   "link_to": "Non Profit Settings",
    "type": "DocType"
   },
   {
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index ba31fee..f27d228 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -756,3 +756,5 @@
 erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl
 erpnext.patches.v13_0.delete_old_bank_reconciliation_doctypes
 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/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/non_profit/doctype/membership_settings/__init__.py b/erpnext/regional/doctype/tax_exemption_80g_certificate/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/regional/doctype/tax_exemption_80g_certificate/__init__.py
diff --git a/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.js b/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.js
new file mode 100644
index 0000000..54cde9c
--- /dev/null
+++ b/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.js
@@ -0,0 +1,67 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Tax Exemption 80G Certificate', {
+	refresh: function(frm) {
+		if (frm.doc.donor) {
+			frm.set_query('donation', function() {
+				return {
+					filters: {
+						docstatus: 1,
+						donor: frm.doc.donor
+					}
+				};
+			});
+		}
+	},
+
+	recipient: function(frm) {
+		if (frm.doc.recipient === 'Donor') {
+			frm.set_value({
+				'member': '',
+				'member_name': '',
+				'member_email': '',
+				'member_pan_number': '',
+				'fiscal_year': '',
+				'total': 0,
+				'payments': []
+			});
+		} else {
+			frm.set_value({
+				'donor': '',
+				'donor_name': '',
+				'donor_email': '',
+				'donor_pan_number': '',
+				'donation': '',
+				'date_of_donation': '',
+				'amount': 0,
+				'mode_of_payment': '',
+				'razorpay_payment_id': ''
+			});
+		}
+	},
+
+	get_payments: function(frm) {
+		frm.call({
+			doc: frm.doc,
+			method: 'get_payments',
+			freeze: true
+		});
+	},
+
+	company: function(frm) {
+		if ((frm.doc.member || frm.doc.donor) && frm.doc.company) {
+			frm.call({
+				doc: frm.doc,
+				method: 'set_company_address',
+				freeze: true
+			});
+		}
+	},
+
+	donation: function(frm) {
+		if (frm.doc.recipient === 'Donor' && !frm.doc.donor) {
+			frappe.msgprint(__('Please select donor first'));
+		}
+	}
+});
diff --git a/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.json b/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.json
new file mode 100644
index 0000000..9eee722
--- /dev/null
+++ b/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.json
@@ -0,0 +1,297 @@
+{
+ "actions": [],
+ "autoname": "naming_series:",
+ "creation": "2021-02-15 12:37:21.577042",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "naming_series",
+  "recipient",
+  "member",
+  "member_name",
+  "member_email",
+  "member_pan_number",
+  "donor",
+  "donor_name",
+  "donor_email",
+  "donor_pan_number",
+  "column_break_4",
+  "date",
+  "fiscal_year",
+  "section_break_11",
+  "company",
+  "company_address",
+  "company_address_display",
+  "column_break_14",
+  "company_pan_number",
+  "company_80g_number",
+  "company_80g_wef",
+  "title",
+  "section_break_6",
+  "get_payments",
+  "payments",
+  "total",
+  "donation_details_section",
+  "donation",
+  "date_of_donation",
+  "amount",
+  "column_break_27",
+  "mode_of_payment",
+  "razorpay_payment_id"
+ ],
+ "fields": [
+  {
+   "fieldname": "recipient",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Certificate Recipient",
+   "options": "Member\nDonor",
+   "reqd": 1
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Member\";",
+   "fieldname": "member",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Member",
+   "mandatory_depends_on": "eval:doc.recipient === \"Member\";",
+   "options": "Member"
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Member\";",
+   "fetch_from": "member.member_name",
+   "fieldname": "member_name",
+   "fieldtype": "Data",
+   "label": "Member Name",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Donor\";",
+   "fieldname": "donor",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Donor",
+   "mandatory_depends_on": "eval:doc.recipient === \"Donor\";",
+   "options": "Donor"
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "date",
+   "fieldtype": "Date",
+   "label": "Date",
+   "reqd": 1
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Member\";",
+   "fieldname": "section_break_6",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "payments",
+   "fieldtype": "Table",
+   "label": "Payments",
+   "options": "Tax Exemption 80G Certificate Detail"
+  },
+  {
+   "fieldname": "total",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Total",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Member\";",
+   "fieldname": "fiscal_year",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Fiscal Year",
+   "options": "Fiscal Year"
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
+  {
+   "fieldname": "get_payments",
+   "fieldtype": "Button",
+   "label": "Get Memberships"
+  },
+  {
+   "fieldname": "naming_series",
+   "fieldtype": "Select",
+   "label": "Naming Series",
+   "options": "NPO-80G-.YYYY.-"
+  },
+  {
+   "fieldname": "section_break_11",
+   "fieldtype": "Section Break",
+   "label": "Company Details"
+  },
+  {
+   "fieldname": "company_address",
+   "fieldtype": "Link",
+   "label": "Company Address",
+   "options": "Address"
+  },
+  {
+   "fieldname": "column_break_14",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fetch_from": "company.pan_details",
+   "fieldname": "company_pan_number",
+   "fieldtype": "Data",
+   "label": "PAN Number",
+   "read_only": 1
+  },
+  {
+   "fieldname": "company_address_display",
+   "fieldtype": "Small Text",
+   "hidden": 1,
+   "label": "Company Address Display",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fetch_from": "company.company_80g_number",
+   "fieldname": "company_80g_number",
+   "fieldtype": "Data",
+   "label": "80G Number",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "company.with_effect_from",
+   "fieldname": "company_80g_wef",
+   "fieldtype": "Date",
+   "label": "80G With Effect From",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Donor\";",
+   "fieldname": "donation_details_section",
+   "fieldtype": "Section Break",
+   "label": "Donation Details"
+  },
+  {
+   "fieldname": "donation",
+   "fieldtype": "Link",
+   "label": "Donation",
+   "mandatory_depends_on": "eval:doc.recipient === \"Donor\";",
+   "options": "Donation"
+  },
+  {
+   "fetch_from": "donation.amount",
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "label": "Amount",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "donation.mode_of_payment",
+   "fieldname": "mode_of_payment",
+   "fieldtype": "Link",
+   "label": "Mode of Payment",
+   "options": "Mode of Payment",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "donation.razorpay_payment_id",
+   "fieldname": "razorpay_payment_id",
+   "fieldtype": "Data",
+   "label": "RazorPay Payment ID",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "donation.date",
+   "fieldname": "date_of_donation",
+   "fieldtype": "Date",
+   "label": "Date of Donation",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_27",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Donor\";",
+   "fetch_from": "donor.donor_name",
+   "fieldname": "donor_name",
+   "fieldtype": "Data",
+   "label": "Donor Name",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Donor\";",
+   "fetch_from": "donor.email",
+   "fieldname": "donor_email",
+   "fieldtype": "Data",
+   "label": "Email",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Member\";",
+   "fetch_from": "member.email_id",
+   "fieldname": "member_email",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Email",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Member\";",
+   "fetch_from": "member.pan_number",
+   "fieldname": "member_pan_number",
+   "fieldtype": "Data",
+   "label": "PAN Details",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.recipient === \"Donor\";",
+   "fetch_from": "donor.pan_number",
+   "fieldname": "donor_pan_number",
+   "fieldtype": "Data",
+   "label": "PAN Details",
+   "read_only": 1
+  },
+  {
+   "fieldname": "title",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Title",
+   "print_hide": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-02-22 00:03:34.215633",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "Tax Exemption 80G Certificate",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "search_fields": "member, member_name",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "title",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.py b/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.py
new file mode 100644
index 0000000..d734a18
--- /dev/null
+++ b/erpnext/regional/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.py
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from frappe.model.document import Document
+from frappe.utils import getdate, flt, get_link_to_form
+from erpnext.accounts.utils import get_fiscal_year
+from frappe.contacts.doctype.address.address import get_company_address
+
+class TaxExemption80GCertificate(Document):
+	def validate(self):
+		self.validate_date()
+		self.validate_duplicates()
+		self.validate_company_details()
+		self.set_company_address()
+		self.set_title()
+
+	def validate_date(self):
+		if self.recipient == 'Member':
+			if getdate(self.date):
+				fiscal_year = get_fiscal_year(fiscal_year=self.fiscal_year, as_dict=True)
+
+				if not (fiscal_year.year_start_date <= getdate(self.date) \
+					<= fiscal_year.year_end_date):
+					frappe.throw(_('The Certificate Date is not in the Fiscal Year {0}').format(frappe.bold(self.fiscal_year)))
+
+	def validate_duplicates(self):
+		if self.recipient == 'Donor':
+			certificate = frappe.db.exists(self.doctype, {'donation': self.donation})
+			if certificate:
+				frappe.throw(_('An 80G Certificate {0} already exists for the donation {1}').format(
+					get_link_to_form(self.doctype, certificate), frappe.bold(self.donation)
+				), title=_('Duplicate Certificate'))
+
+	def validate_company_details(self):
+		fields = ['company_80g_number', 'with_effect_from', 'pan_details']
+		company_details = frappe.db.get_value('Company', self.company, fields, as_dict=True)
+		if not company_details.company_80g_number:
+			frappe.throw(_('Please set the {0} for company {1}').format(frappe.bold('80G Number'),
+				get_link_to_form('Company', self.company)))
+
+		if not company_details.pan_details:
+			frappe.throw(_('Please set the {0} for company {1}').format(frappe.bold('PAN Number'),
+				get_link_to_form('Company', self.company)))
+
+	def set_company_address(self):
+		address = get_company_address(self.company)
+		self.company_address = address.company_address
+		self.company_address_display = address.company_address_display
+
+	def set_title(self):
+		if self.recipient == "Member":
+			self.title = self.member_name
+		else:
+			self.title = self.donor_name
+
+	def get_payments(self):
+		if not self.member:
+			frappe.throw(_('Please select a Member first.'))
+
+		fiscal_year = get_fiscal_year(fiscal_year=self.fiscal_year, as_dict=True)
+
+		memberships = frappe.db.get_all('Membership', {
+			'member': self.member,
+			'from_date': ['between', (fiscal_year.year_start_date, fiscal_year.year_end_date)],
+			'to_date': ['between', (fiscal_year.year_start_date, fiscal_year.year_end_date)],
+			'membership_status': ('!=', 'Cancelled')
+		}, ['from_date', 'amount', 'name', 'invoice', 'payment_id'])
+
+		if not memberships:
+			frappe.msgprint(_('No Membership Payments found against the Member {0}').format(self.member))
+
+		total = 0
+		self.payments = []
+
+		for doc in memberships:
+			self.append('payments', {
+				'date': doc.from_date,
+				'amount': doc.amount,
+				'invoice_id': doc.invoice,
+				'razorpay_payment_id': doc.payment_id,
+				'membership': doc.name
+			})
+			total += flt(doc.amount)
+
+		self.total = total
diff --git a/erpnext/regional/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py b/erpnext/regional/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py
new file mode 100644
index 0000000..346ebbf
--- /dev/null
+++ b/erpnext/regional/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+from frappe.utils import getdate
+from erpnext.accounts.utils import get_fiscal_year
+from erpnext.non_profit.doctype.donation.test_donation import create_donor, create_mode_of_payment, create_donor_type
+from erpnext.non_profit.doctype.donation.donation import create_donation
+from erpnext.non_profit.doctype.membership.test_membership import setup_membership, make_membership
+from erpnext.non_profit.doctype.member.member import create_member
+
+class TestTaxExemption80GCertificate(unittest.TestCase):
+	def setUp(self):
+		frappe.db.sql('delete from `tabTax Exemption 80G Certificate`')
+		frappe.db.sql('delete from `tabMembership`')
+		create_donor_type()
+		settings = frappe.get_doc('Non Profit Settings')
+		settings.company = '_Test Company'
+		settings.donation_company = '_Test Company'
+		settings.default_donor_type = '_Test Donor'
+		settings.creation_user = 'Administrator'
+		settings.save()
+
+		company = frappe.get_doc('Company', '_Test Company')
+		company.pan_details = 'BBBTI3374C'
+		company.company_80g_number = 'NQ.CIT(E)I2018-19/DEL-IE28615-27062018/10087'
+		company.with_effect_from = getdate()
+		company.save()
+
+	def test_duplicate_donation_certificate(self):
+		donor = create_donor()
+		create_mode_of_payment()
+		payment = frappe._dict({
+			'amount': 100,
+			'method': 'Debit Card',
+			'id': 'pay_MeXAmsgeKOhq7O'
+		})
+		donation = create_donation(donor, payment)
+
+		args = frappe._dict({
+			'recipient': 'Donor',
+			'donor': donor.name,
+			'donation': donation.name
+		})
+		certificate = create_80g_certificate(args)
+		certificate.insert()
+
+		# check company details
+		self.assertEquals(certificate.company_pan_number, 'BBBTI3374C')
+		self.assertEquals(certificate.company_80g_number, 'NQ.CIT(E)I2018-19/DEL-IE28615-27062018/10087')
+
+		# check donation details
+		self.assertEquals(certificate.amount, donation.amount)
+
+		duplicate_certificate = create_80g_certificate(args)
+		# duplicate validation
+		self.assertRaises(frappe.ValidationError, duplicate_certificate.insert)
+
+	def test_membership_80g_certificate(self):
+		plan = setup_membership()
+
+		# make test member
+		member_doc = create_member(frappe._dict({
+			'fullname': "_Test_Member",
+			'email': "_test_member_erpnext@example.com",
+			'plan_id': plan.name
+		}))
+		member_doc.make_customer_and_link()
+		member = member_doc.name
+
+		membership = make_membership(member, { "from_date": getdate() })
+		invoice = membership.generate_invoice(save=True)
+
+		args = frappe._dict({
+			'recipient': 'Member',
+			'member': member,
+			'fiscal_year': get_fiscal_year(getdate(), as_dict=True).get('name')
+		})
+		certificate = create_80g_certificate(args)
+		certificate.get_payments()
+		certificate.insert()
+
+		self.assertEquals(len(certificate.payments), 1)
+		self.assertEquals(certificate.payments[0].amount, membership.amount)
+		self.assertEquals(certificate.payments[0].invoice_id, invoice.name)
+
+
+def create_80g_certificate(args):
+	certificate = frappe.get_doc({
+		'doctype': 'Tax Exemption 80G Certificate',
+		'recipient': args.recipient,
+		'date': getdate(),
+		'company': '_Test Company'
+	})
+
+	certificate.update(args)
+
+	return certificate
\ No newline at end of file
diff --git a/erpnext/non_profit/doctype/membership_settings/__init__.py b/erpnext/regional/doctype/tax_exemption_80g_certificate_detail/__init__.py
similarity index 100%
copy from erpnext/non_profit/doctype/membership_settings/__init__.py
copy to erpnext/regional/doctype/tax_exemption_80g_certificate_detail/__init__.py
diff --git a/erpnext/regional/doctype/tax_exemption_80g_certificate_detail/tax_exemption_80g_certificate_detail.json b/erpnext/regional/doctype/tax_exemption_80g_certificate_detail/tax_exemption_80g_certificate_detail.json
new file mode 100644
index 0000000..dfa817d
--- /dev/null
+++ b/erpnext/regional/doctype/tax_exemption_80g_certificate_detail/tax_exemption_80g_certificate_detail.json
@@ -0,0 +1,66 @@
+{
+ "actions": [],
+ "creation": "2021-02-15 12:43:52.754124",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "date",
+  "amount",
+  "invoice_id",
+  "column_break_4",
+  "razorpay_payment_id",
+  "membership"
+ ],
+ "fields": [
+  {
+   "fieldname": "date",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "label": "Date",
+   "reqd": 1
+  },
+  {
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Amount",
+   "reqd": 1
+  },
+  {
+   "fieldname": "invoice_id",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Invoice ID",
+   "options": "Sales Invoice",
+   "reqd": 1
+  },
+  {
+   "fieldname": "razorpay_payment_id",
+   "fieldtype": "Data",
+   "label": "Razorpay Payment ID"
+  },
+  {
+   "fieldname": "membership",
+   "fieldtype": "Link",
+   "label": "Membership",
+   "options": "Membership"
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-02-15 16:35:10.777587",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "Tax Exemption 80G Certificate Detail",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/tax_exemption_80g_certificate_detail/tax_exemption_80g_certificate_detail.py b/erpnext/regional/doctype/tax_exemption_80g_certificate_detail/tax_exemption_80g_certificate_detail.py
new file mode 100644
index 0000000..bdad798
--- /dev/null
+++ b/erpnext/regional/doctype/tax_exemption_80g_certificate_detail/tax_exemption_80g_certificate_detail.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class TaxExemption80GCertificateDetail(Document):
+	pass
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index ee46a52..fa197e3 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -399,9 +399,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,
@@ -499,6 +499,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',
@@ -581,7 +589,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',
@@ -643,7 +659,7 @@
 		pass
 
 	docs = get_tds_details(accounts, fiscal_year)
-	
+
 	for d in docs:
 		try:
 			doc = frappe.get_doc(d)
@@ -661,7 +677,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/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/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")},