Merge branch 'develop' into naming-series-proj
diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json
index 85c9209..b2a3f83 100644
--- a/erpnext/accounts/desk_page/accounting/accounting.json
+++ b/erpnext/accounts/desk_page/accounting/accounting.json
@@ -79,6 +79,11 @@
    "hidden": 0,
    "label": "Profitability",
    "links": "[\n    {\n        \"dependencies\": [\n            \"Sales Invoice\"\n        ],\n        \"doctype\": \"Sales Invoice\",\n        \"is_query_report\": true,\n        \"label\": \"Gross Profit\",\n        \"name\": \"Gross Profit\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"GL Entry\"\n        ],\n        \"doctype\": \"GL Entry\",\n        \"is_query_report\": true,\n        \"label\": \"Profitability Analysis\",\n        \"name\": \"Profitability Analysis\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Sales Invoice\"\n        ],\n        \"doctype\": \"Sales Invoice\",\n        \"is_query_report\": true,\n        \"label\": \"Sales Invoice Trends\",\n        \"name\": \"Sales Invoice Trends\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Purchase Invoice\"\n        ],\n        \"doctype\": \"Purchase Invoice\",\n        \"is_query_report\": true,\n        \"label\": \"Purchase Invoice Trends\",\n        \"name\": \"Purchase Invoice Trends\",\n        \"type\": \"report\"\n    }\n]"
+  },
+  {
+   "hidden": 0,
+   "label": "Value-Added Tax (VAT UAE)",
+   "links": "[\n    {\n        \"country\": \"United Arab Emirates\",\n        \"label\": \"UAE VAT Settings\",\n        \"name\": \"UAE VAT Settings\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"country\": \"United Arab Emirates\",\n        \"is_query_report\": true,\n        \"label\": \"UAE VAT 201\",\n        \"name\": \"UAE VAT 201\",\n        \"type\": \"report\"\n    }\n\n]"
   }
  ],
  "category": "Modules",
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json
index 4573c50..b7bbb74 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.json
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json
@@ -1,5 +1,6 @@
 {
  "actions": [],
+ "allow_auto_repeat": 1,
  "allow_import": 1,
  "autoname": "naming_series:",
  "creation": "2013-03-25 10:53:52",
@@ -503,7 +504,7 @@
  "idx": 176,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-06-02 18:15:46.955697",
+ "modified": "2020-10-30 13:56:01.121995",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Journal Entry",
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json
index 72149a6..2e1f201 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.json
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json
@@ -1,5 +1,6 @@
 {
  "actions": [],
+ "allow_auto_repeat": 1,
  "allow_import": 1,
  "autoname": "naming_series:",
  "creation": "2016-06-01 14:38:51.012597",
@@ -587,7 +588,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-09-02 13:39:43.383705",
+ "modified": "2020-10-30 13:56:20.007336",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Entry",
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
index 1cff3c6..5bc57b4 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
@@ -1,5 +1,6 @@
 {
  "actions": [],
+ "allow_auto_repeat": 1,
  "allow_import": 1,
  "autoname": "naming_series:",
  "creation": "2020-01-24 15:29:29.933693",
@@ -1580,7 +1581,7 @@
  "icon": "fa fa-file-text",
  "is_submittable": 1,
  "links": [],
- "modified": "2020-09-28 16:51:24.641755",
+ "modified": "2020-10-30 13:56:51.056083",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Invoice",
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index a7e20a0..d486ff6 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -39,6 +39,7 @@
 		self.validate_serialised_or_batched_item()
 		self.validate_stock_availablility()
 		self.validate_return_items_qty()
+		self.validate_non_stock_items()
 		self.set_status()
 		self.set_account_for_mode_of_payment()
 		self.validate_pos()
@@ -174,6 +175,14 @@
 							_("Row #{}: Serial No {} cannot be returned since it was not transacted in original invoice {}")
 							.format(d.idx, bold_serial_no, bold_return_against)
 						)
+	
+	def validate_non_stock_items(self):
+		for d in self.get("items"):
+			is_stock_item = frappe.get_cached_value("Item", d.get("item_code"), "is_stock_item")
+			if not is_stock_item:
+				frappe.throw(_("Row #{}: Item {} is a non stock item. You can only include stock items in a POS Invoice. ").format(
+					d.idx, frappe.bold(d.item_code)
+				), title=_("Invalid Item"))
 
 	def validate_mode_of_payment(self):
 		if len(self.payments) == 0:
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 8925b87..2df77a8 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -1,5 +1,6 @@
 {
  "actions": [],
+ "allow_auto_repeat": 1,
  "allow_import": 1,
  "autoname": "naming_series:",
  "creation": "2013-05-21 16:16:39",
@@ -1334,7 +1335,8 @@
  "icon": "fa fa-file-text",
  "idx": 204,
  "is_submittable": 1,
- "modified": "2020-09-21 12:22:09.164068",
+ "links": [],
+ "modified": "2020-10-30 13:57:18.266978",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice",
@@ -1396,4 +1398,4 @@
  "timeline_field": "supplier",
  "title_field": "title",
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 26acad3..fb30153 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -1003,7 +1003,7 @@
 		'expense_account': args.expense_account or '_Test Account Cost for Goods Sold - _TC',
 		"conversion_factor": 1.0,
 		"serial_no": args.serial_no,
-		"stock_uom": "_Test UOM",
+		"stock_uom": args.uom or "_Test UOM",
 		"cost_center": args.cost_center or "_Test Cost Center - _TC",
 		"project": args.project,
 		"rejected_warehouse": args.rejected_warehouse or "",
@@ -1045,7 +1045,8 @@
 	pi.is_return = args.is_return
 	pi.credit_to = args.return_against or "Creditors - _TC"
 	pi.is_subcontracted = args.is_subcontracted or "No"
-	pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
+	if args.supplier_warehouse: 
+		pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
 
 	pi.append("items", {
 		"item_code": args.item or args.item_code or "_Test Item",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index ae40153..17fbe2d 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -1,5 +1,6 @@
 {
  "actions": [],
+ "allow_auto_repeat": 1,
  "allow_import": 1,
  "autoname": "naming_series:",
  "creation": "2013-05-24 19:29:05",
@@ -1955,7 +1956,7 @@
  "idx": 181,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-10-09 15:59:57.544736",
+ "modified": "2020-10-30 13:57:45.086303",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice",
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index 4b865a9..71231f6 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -1,5 +1,6 @@
 {
  "actions": [],
+ "allow_auto_repeat": 1,
  "allow_import": 1,
  "autoname": "naming_series:",
  "creation": "2013-05-21 16:16:39",
@@ -1105,7 +1106,7 @@
  "idx": 105,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-10-30 11:39:37.388249",
+ "modified": "2020-10-30 13:58:14.697921",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order",
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
index 9a092ca..b39c989 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
@@ -1,5 +1,6 @@
 {
  "actions": [],
+ "allow_auto_repeat": 1,
  "allow_import": 1,
  "autoname": "naming_series:",
  "creation": "2013-05-21 16:16:45",
@@ -807,7 +808,7 @@
  "idx": 29,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-10-01 20:56:17.932007",
+ "modified": "2020-10-30 13:58:33.043971",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Supplier Quotation",
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 78ef665..b4c57d7 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -250,7 +250,11 @@
 		"on_trash": "erpnext.regional.check_deletion_permission"
 	},
 	"Purchase Invoice": {
-		"validate": "erpnext.regional.india.utils.update_grand_total_for_rcm"
+		"validate": [
+			"erpnext.regional.india.utils.update_grand_total_for_rcm",
+			"erpnext.regional.united_arab_emirates.utils.update_grand_total_for_rcm",
+			"erpnext.regional.united_arab_emirates.utils.validate_returns"
+			]
 	},
 	"Payment Entry": {
 		"on_submit": ["erpnext.regional.create_transaction_log", "erpnext.accounts.doctype.payment_request.payment_request.update_payment_req_status", "erpnext.accounts.doctype.dunning.dunning.resolve_dunning"],
@@ -391,7 +395,8 @@
 		'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.india.utils.make_regional_gl_entries'
 	},
 	'United Arab Emirates': {
-		'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data'
+		'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data',
+		'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.united_arab_emirates.utils.make_regional_gl_entries',
 	},
 	'Saudi Arabia': {
 		'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data'
diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
index 8913c64..c1dcc97 100644
--- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py
+++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
@@ -32,7 +32,7 @@
 		additional_salary.employee = self.employee
 		earning_component = frappe.get_value("Leave Type", self.leave_type, "earning_component")
 		if not earning_component:
-			frappe.throw(_("Please set Earning Component for Leave type: {0}.".format(self.leave_type)))
+			frappe.throw(_("Please set Earning Component for Leave type: {0}.").format(self.leave_type))
 		additional_salary.salary_component = earning_component
 		additional_salary.payroll_date = self.encashment_date
 		additional_salary.amount = self.encashment_amount
@@ -98,7 +98,11 @@
 		create_leave_ledger_entry(self, args, submit)
 
 		# create reverse entry for expired leaves
-		to_date = self.get_leave_allocation().get('to_date')
+		leave_allocation = self.get_leave_allocation()
+		if not leave_allocation:
+			return
+
+		to_date = leave_allocation.get('to_date')
 		if to_date < getdate(nowdate()):
 			args = frappe._dict(
 				leaves=self.encashable_days,
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.py b/erpnext/loan_management/doctype/loan_application/loan_application.py
index bac6e63..e59db4c 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application.py
+++ b/erpnext/loan_management/doctype/loan_application/loan_application.py
@@ -127,6 +127,7 @@
 		target_doc.loan_account = account_details.loan_account
 		target_doc.interest_income_account = account_details.interest_income_account
 		target_doc.penalty_income_account = account_details.penalty_income_account
+		target_doc.loan_application = source_name
 
 
 	doclist = get_mapped_doc("Loan Application", source_name, {
diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py
index 4c85cb6..7d15aba 100644
--- a/erpnext/non_profit/doctype/membership/membership.py
+++ b/erpnext/non_profit/doctype/membership/membership.py
@@ -70,7 +70,7 @@
 		settings = frappe.get_doc("Membership Settings")
 
 		if not member.customer:
-			frappe.throw(_("No customer linked to member {}", [member.name]))
+			frappe.throw(_("No customer linked to member {0}").format(frappe.bold(self.member)))
 
 		if not settings.debit_account:
 			frappe.throw(_("You need to set <b>Debit Account</b> in Membership Settings"))
diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py
index 7afde37..fe072d7 100644
--- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py
+++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py
@@ -51,7 +51,7 @@
 
 	for encashment in leave_encashments:
 		if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Encashment', 'transaction_name': encashment.name}):
-			frappe.get_doc("Leave Enchashment", encashment).create_leave_ledger_entry()
+			frappe.get_doc("Leave Encashment", encashment).create_leave_ledger_entry()
 
 def generate_expiry_allocation_ledger_entries():
 	''' fix ledger entries for missing leave allocation transaction '''
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
index 30ea432..a3d12c3 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
@@ -344,9 +344,13 @@
 		employees_to_mark_attendance = []
 		days_in_payroll, days_holiday, days_attendance_marked = 0, 0, 0
 		for employee_detail in self.employees:
-			days_holiday = self.get_count_holidays_of_employee(employee_detail.employee)
-			days_attendance_marked = self.get_count_employee_attendance(employee_detail.employee)
-			days_in_payroll = date_diff(self.end_date, self.start_date) + 1
+			employee_joining_date = frappe.db.get_value("Employee", employee_detail.employee, 'date_of_joining')
+			start_date = self.start_date
+			if employee_joining_date > getdate(self.start_date):
+				start_date = employee_joining_date
+			days_holiday = self.get_count_holidays_of_employee(employee_detail.employee, start_date)
+			days_attendance_marked = self.get_count_employee_attendance(employee_detail.employee, start_date)
+			days_in_payroll = date_diff(self.end_date, start_date) + 1
 			if days_in_payroll > days_holiday + days_attendance_marked:
 				employees_to_mark_attendance.append({
 					"employee": employee_detail.employee,
@@ -354,22 +358,25 @@
 					})
 		return employees_to_mark_attendance
 
-	def get_count_holidays_of_employee(self, employee):
+	def get_count_holidays_of_employee(self, employee, start_date):
 		holiday_list = get_holiday_list_for_employee(employee)
 		holidays = 0
 		if holiday_list:
 			days = frappe.db.sql("""select count(*) from tabHoliday where
 				parent=%s and holiday_date between %s and %s""", (holiday_list,
-				self.start_date, self.end_date))
+				start_date, self.end_date))
 			if days and days[0][0]:
 				holidays = days[0][0]
 		return holidays
 
-	def get_count_employee_attendance(self, employee):
+	def get_count_employee_attendance(self, employee, start_date):
 		marked_days = 0
-		attendances = frappe.db.sql("""select count(*) from tabAttendance where
-			employee=%s and docstatus=1 and attendance_date between %s and %s""",
-			(employee, self.start_date, self.end_date))
+		attendances = frappe.get_all("Attendance",
+				fields = ["count(*)"],
+				filters = {
+					"employee": employee,
+					"attendance_date": ('between', [start_date, self.end_date])
+				}, as_list=1)
 		if attendances and attendances[0][0]:
 			marked_days = attendances[0][0]
 		return marked_days
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index 4ccf564..cecb8cd 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -240,7 +240,6 @@
 				self.absent_days += unmarked_days #will be treated as absent
 				self.payment_days -= unmarked_days
 				if include_holidays_in_total_working_days:
-					self.absent_days -= len(holidays)
 					for holiday in holidays:
 						if not frappe.db.exists("Attendance", {"employee": self.employee, "attendance_date": holiday, "docstatus": 1 }):
 							self.payment_days += 1
diff --git a/erpnext/payroll/report/bank_remittance/bank_remittance.py b/erpnext/payroll/report/bank_remittance/bank_remittance.py
index 4b052bf..500543c 100644
--- a/erpnext/payroll/report/bank_remittance/bank_remittance.py
+++ b/erpnext/payroll/report/bank_remittance/bank_remittance.py
@@ -47,33 +47,39 @@
 			"fieldtype": "Int",
 			"fieldname": "employee_account_no",
 			"width": 50
-		},
-		{
+		}
+	]
+
+	if frappe.db.has_column('Employee', 'ifsc_code'):
+		columns.append({
 			"label": _("IFSC Code"),
 			"fieldtype": "Data",
 			"fieldname": "bank_code",
 			"width": 100
-		},
-		{
-			"label": _("Currency"),
-			"fieldtype": "Data",
-			"fieldname": "currency",
-			"width": 50
-		},
-		{
-			"label": _("Net Salary Amount"),
-			"fieldtype": "Currency",
-			"options": "currency",
-			"fieldname": "amount",
-			"width": 100
-		}
-	]
+		})
+
+	columns += [{
+		"label": _("Currency"),
+		"fieldtype": "Data",
+		"fieldname": "currency",
+		"width": 50
+	},
+	{
+		"label": _("Net Salary Amount"),
+		"fieldtype": "Currency",
+		"options": "currency",
+		"fieldname": "amount",
+		"width": 100
+	}]
+
 	data = []
 
 	accounts = get_bank_accounts()
 	payroll_entries = get_payroll_entries(accounts, filters)
 	salary_slips = get_salary_slips(payroll_entries)
-	get_emp_bank_ifsc_code(salary_slips)
+
+	if frappe.db.has_column('Employee', 'ifsc_code'):
+		get_emp_bank_ifsc_code(salary_slips)
 
 	for salary in salary_slips:
 		if salary.bank_name and salary.bank_account_no and salary.debit_acc_no and salary.status in ["Submitted", "Paid"]:
diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js
index 6e97d81..29f3595 100644
--- a/erpnext/public/js/controllers/accounts.js
+++ b/erpnext/public/js/controllers/accounts.js
@@ -146,18 +146,18 @@
 	if(!d.charge_type && d.account_head){
 		frappe.msgprint(__("Please select Charge Type first"));
 		frappe.model.set_value(cdt, cdn, "account_head", "");
-	} else if(d.account_head && d.charge_type!=="Actual") {
+	} else if (d.account_head) {
 		frappe.call({
 			type:"GET",
 			method: "erpnext.controllers.accounts_controller.get_tax_rate",
 			args: {"account_head":d.account_head},
 			callback: function(r) {
-				frappe.model.set_value(cdt, cdn, "rate", r.message.tax_rate || 0);
+				if (d.charge_type!=="Actual") {
+					frappe.model.set_value(cdt, cdn, "rate", r.message.tax_rate || 0);
+				}
 				frappe.model.set_value(cdt, cdn, "description", r.message.account_name);
 			}
 		})
-	} else if (d.charge_type == 'Actual' && d.account_head) {
-		frappe.model.set_value(cdt, cdn, "description", d.account_head.split(' - ')[0]);
 	}
 }
 
diff --git a/erpnext/regional/doctype/uae_vat_account/__init__.py b/erpnext/regional/doctype/uae_vat_account/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/regional/doctype/uae_vat_account/__init__.py
diff --git a/erpnext/regional/doctype/uae_vat_account/uae_vat_account.json b/erpnext/regional/doctype/uae_vat_account/uae_vat_account.json
new file mode 100644
index 0000000..73a8169
--- /dev/null
+++ b/erpnext/regional/doctype/uae_vat_account/uae_vat_account.json
@@ -0,0 +1,35 @@
+{
+ "actions": [],
+ "autoname": "account",
+ "creation": "2020-09-28 11:30:45.472053",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "account"
+ ],
+ "fields": [
+  {
+   "allow_in_quick_entry": 1,
+   "fieldname": "account",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_preview": 1,
+   "label": "Account",
+   "options": "Account"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2020-09-28 12:02:56.444007",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "UAE VAT Account",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/uae_vat_account/uae_vat_account.py b/erpnext/regional/doctype/uae_vat_account/uae_vat_account.py
new file mode 100644
index 0000000..80d6b3a
--- /dev/null
+++ b/erpnext/regional/doctype/uae_vat_account/uae_vat_account.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class UAEVATAccount(Document):
+	pass
diff --git a/erpnext/regional/doctype/uae_vat_settings/__init__.py b/erpnext/regional/doctype/uae_vat_settings/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/regional/doctype/uae_vat_settings/__init__.py
diff --git a/erpnext/regional/doctype/uae_vat_settings/test_uae_vat_settings.py b/erpnext/regional/doctype/uae_vat_settings/test_uae_vat_settings.py
new file mode 100644
index 0000000..b88439f
--- /dev/null
+++ b/erpnext/regional/doctype/uae_vat_settings/test_uae_vat_settings.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestUAEVATSettings(unittest.TestCase):
+	pass
diff --git a/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.js b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.js
new file mode 100644
index 0000000..07a9301
--- /dev/null
+++ b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('UAE VAT Settings', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.json b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.json
new file mode 100644
index 0000000..ce2c1d4
--- /dev/null
+++ b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.json
@@ -0,0 +1,55 @@
+{
+ "actions": [],
+ "autoname": "field:company",
+ "creation": "2020-09-25 12:48:51.463265",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "company",
+  "uae_vat_accounts"
+ ],
+ "fields": [
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1,
+   "unique": 1
+  },
+  {
+   "fieldname": "uae_vat_accounts",
+   "fieldtype": "Table",
+   "label": "UAE VAT Accounts",
+   "options": "UAE VAT Account",
+   "reqd": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2020-09-30 20:08:18.764798",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "UAE VAT Settings",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.py b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.py
new file mode 100644
index 0000000..20dc604
--- /dev/null
+++ b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class UAEVATSettings(Document):
+	pass
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 6164e06..5458001 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -516,6 +516,9 @@
 		data.transType = 1
 		data.actualToStateCode = data.toStateCode
 		shipping_address = billing_address
+	
+	if doc.gst_category == 'SEZ':
+		data.toStateCode = 99
 
 	return data
 
diff --git a/erpnext/regional/report/uae_vat_201/__init__.py b/erpnext/regional/report/uae_vat_201/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/regional/report/uae_vat_201/__init__.py
diff --git a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py
new file mode 100644
index 0000000..daa6976
--- /dev/null
+++ b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py
@@ -0,0 +1,239 @@
+# coding=utf-8
+from __future__ import unicode_literals
+
+import erpnext
+import frappe
+from unittest import TestCase
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse_account
+from erpnext.regional.report.uae_vat_201.uae_vat_201 import (
+	get_total_emiratewise,
+	get_tourist_tax_return_total,
+	get_tourist_tax_return_tax,
+	get_zero_rated_total,
+	get_exempt_total,
+	get_standard_rated_expenses_total,
+	get_standard_rated_expenses_tax,
+)
+
+test_dependencies = ["Territory", "Customer Group", "Supplier Group", "Item"]
+
+class TestUaeVat201(TestCase):
+	def setUp(self):
+		frappe.set_user("Administrator")
+
+		frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company UAE VAT'")
+		frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company UAE VAT'")
+
+		make_company("_Test Company UAE VAT", "_TCUV")
+		set_vat_accounts()
+
+		make_customer()
+
+		make_supplier()
+
+		create_warehouse("_Test UAE VAT Supplier Warehouse", company="_Test Company UAE VAT")
+
+		make_item("_Test UAE VAT Item", properties = {"is_zero_rated": 0, "is_exempt": 0})
+		make_item("_Test UAE VAT Zero Rated Item", properties = {"is_zero_rated": 1, "is_exempt": 0})
+		make_item("_Test UAE VAT Exempt Item", properties = {"is_zero_rated": 0, "is_exempt": 1})
+
+		make_sales_invoices()
+
+		create_purchase_invoices()
+
+	def test_uae_vat_201_report(self):
+		filters = {"company": "_Test Company UAE VAT"}
+		total_emiratewise = get_total_emiratewise(filters)
+		amounts_by_emirate = {}
+		for data in total_emiratewise:
+			emirate, amount, vat = data
+			amounts_by_emirate[emirate] = {
+				"raw_amount": amount,
+				"raw_vat_amount": vat,
+			}
+		self.assertEqual(amounts_by_emirate["Sharjah"]["raw_amount"],100)
+		self.assertEqual(amounts_by_emirate["Sharjah"]["raw_vat_amount"],5)
+		self.assertEqual(amounts_by_emirate["Dubai"]["raw_amount"],200)
+		self.assertEqual(amounts_by_emirate["Dubai"]["raw_vat_amount"],10)
+		self.assertEqual(get_tourist_tax_return_total(filters),100)
+		self.assertEqual(get_tourist_tax_return_tax(filters),2)
+		self.assertEqual(get_zero_rated_total(filters),100)
+		self.assertEqual(get_exempt_total(filters),100)
+		self.assertEqual(get_standard_rated_expenses_total(filters),250)
+		self.assertEqual(get_standard_rated_expenses_tax(filters),1)
+
+def make_company(company_name, abbr):
+	if not frappe.db.exists("Company", company_name):
+		company = frappe.get_doc({
+			"doctype": "Company",
+			"company_name": company_name,
+			"abbr": abbr,
+			"default_currency": "AED",
+			"country": "United Arab Emirates",
+			"create_chart_of_accounts_based_on": "Standard Template",
+		})
+		company.insert()
+	else:
+		company = frappe.get_doc("Company", company_name)
+
+	company.create_default_warehouses()
+
+	if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": company.name}):
+		company.create_default_cost_center()
+
+	company.save()
+	return company
+
+def set_vat_accounts():
+	if not frappe.db.exists("UAE VAT Settings", "_Test Company UAE VAT"):
+		vat_accounts = frappe.get_all(
+			"Account",
+			fields=["name"],
+			filters = {
+				"company": "_Test Company UAE VAT",
+				"is_group": 0,
+				"account_type": "Tax"
+			}
+		)
+
+		uae_vat_accounts = []
+		for account in vat_accounts:
+			uae_vat_accounts.append({
+				"doctype": "UAE VAT Account",
+				"account": account.name
+			})
+
+		frappe.get_doc({
+			"company": "_Test Company UAE VAT",
+			"uae_vat_accounts": uae_vat_accounts,
+			"doctype": "UAE VAT Settings",
+		}).insert()
+
+def make_customer():
+	if not frappe.db.exists("Customer", "_Test UAE Customer"):
+		customer = frappe.get_doc({
+			"doctype": "Customer",
+			"customer_name": "_Test UAE Customer",
+			"customer_type": "Company",
+		})
+		customer.insert()
+	else:
+		customer = frappe.get_doc("Customer", "_Test UAE Customer")
+
+def make_supplier():
+	if not frappe.db.exists("Supplier", "_Test UAE Supplier"):
+		frappe.get_doc({
+			"supplier_group": "Local",
+			"supplier_name": "_Test UAE Supplier",
+			"supplier_type": "Individual",
+			"doctype": "Supplier",
+		}).insert()
+
+def create_warehouse(warehouse_name, properties=None, company=None):
+	if not company:
+		company = "_Test Company"
+
+	warehouse_id = erpnext.encode_company_abbr(warehouse_name, company)
+	if not frappe.db.exists("Warehouse", warehouse_id):
+		warehouse = frappe.new_doc("Warehouse")
+		warehouse.warehouse_name = warehouse_name
+		warehouse.parent_warehouse = "All Warehouses - _TCUV"
+		warehouse.company = company
+		warehouse.account = get_warehouse_account(warehouse_name, company)
+		if properties:
+			warehouse.update(properties)
+		warehouse.save()
+		return warehouse.name
+	else:
+		return warehouse_id
+
+def make_item(item_code, properties=None):
+	if frappe.db.exists("Item", item_code):
+		return frappe.get_doc("Item", item_code)
+
+	item = frappe.get_doc({
+		"doctype": "Item",
+		"item_code": item_code,
+		"item_name": item_code,
+		"description": item_code,
+		"item_group": "Products"
+	})
+
+	if properties:
+		item.update(properties)
+
+	item.insert()
+
+	return item
+
+def make_sales_invoices():
+	def make_sales_invoices_wrapper(emirate, item, tax = True, tourist_tax= False):
+		si = create_sales_invoice(
+			company="_Test Company UAE VAT",
+			customer = '_Test UAE Customer',
+			currency = 'AED',
+			warehouse = 'Finished Goods - _TCUV',
+			debit_to = 'Debtors - _TCUV',
+			income_account = 'Sales - _TCUV',
+			expense_account = 'Cost of Goods Sold - _TCUV',
+			cost_center = 'Main - _TCUV',
+			item = item,
+			do_not_save=1
+		)
+		si.vat_emirate = emirate
+		if tax:
+			si.append(
+				"taxes", {
+					"charge_type": "On Net Total",
+					"account_head": "VAT 5% - _TCUV",
+					"cost_center": "Main - _TCUV",
+					"description": "VAT 5% @ 5.0",
+					"rate": 5.0
+				}
+			)
+		if tourist_tax:
+			si.tourist_tax_return = 2
+		si.submit()
+
+	#Define Item Names
+	uae_item = "_Test UAE VAT Item"
+	uae_exempt_item = "_Test UAE VAT Exempt Item"
+	uae_zero_rated_item = "_Test UAE VAT Zero Rated Item"
+
+	#Sales Invoice with standard rated expense in Dubai
+	make_sales_invoices_wrapper('Dubai', uae_item)
+	#Sales Invoice with standard rated expense in Sharjah
+	make_sales_invoices_wrapper('Sharjah', uae_item)
+	#Sales Invoice with Tourist Tax Return
+	make_sales_invoices_wrapper('Dubai', uae_item, True, True)
+	#Sales Invoice with Exempt Item
+	make_sales_invoices_wrapper('Sharjah', uae_exempt_item, False)
+	#Sales Invoice with Zero Rated Item
+	make_sales_invoices_wrapper('Sharjah', uae_zero_rated_item, False)
+
+def create_purchase_invoices():
+	pi = make_purchase_invoice(
+		company="_Test Company UAE VAT",
+		supplier = '_Test UAE Supplier',
+		supplier_warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV',
+		warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV',
+		currency = 'AED',
+		cost_center = 'Main - _TCUV',
+		expense_account = 'Cost of Goods Sold - _TCUV',
+		item = "_Test UAE VAT Item",
+		do_not_save=1,
+		uom = "Nos"
+	)
+	pi.append("taxes", {
+		"charge_type": "On Net Total",
+		"account_head": "VAT 5% - _TCUV",
+		"cost_center": "Main - _TCUV",
+		"description": "VAT 5% @ 5.0",
+		"rate": 5.0
+	})
+
+	pi.recoverable_standard_rated_expenses = 1
+
+	pi.submit()
diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.html b/erpnext/regional/report/uae_vat_201/uae_vat_201.html
new file mode 100644
index 0000000..d9b9968
--- /dev/null
+++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.html
@@ -0,0 +1,77 @@
+{%
+	var report_columns = report.get_columns_for_print();
+	report_columns = report_columns.filter(col => !col.hidden);
+%}
+<style>
+	.print-format {
+		padding: 10mm;
+		font-size: 8.0pt !important;
+		font-family: Tahoma, sans-serif;
+	}
+</style>
+
+<h1  style="margin-top:0; text-align: center;">{%= __(report.report_name) %}</h1>
+
+<h3  style="margin-top:0; font-weight:500">{%= __("VAT on Sales and All Other Outputs") %}</h2>
+
+<table class="table table-bordered">
+
+    <thead>
+        <th style="width: 13">{%= report_columns[0].label %}</th>
+        <th style="width: {%= 100 - (report_columns.length - 1) * 13%}%">{%= report_columns[1].label %}</th>
+
+        {% for (let i=2; i<report_columns.length; i++) { %}
+            <th style="width: 13">{%= report_columns[i].label %}</th>
+        {% } %}
+    </thead>
+
+    <tbody>
+        {% for (let j=1; j<12; j++)  { %}
+        {%
+            var row = data[j];
+        %}
+        <tr >
+            {% for (let i=0; i<report_columns.length; i++) { %}
+                <td >
+                    {% const fieldname = report_columns[i].fieldname; %}
+                    {% if (!is_null(row[fieldname])) { %}
+                        {%= frappe.format(row[fieldname], report_columns[i], {}, row) %}
+                    {% } %}
+                </td>
+            {% } %}
+        </tr>
+        {% } %}
+    </tbody>
+</table>
+
+<h3 style="margin-top:0; font-weight:500">{%= __("VAT on Expenses and All Other Inputs") %}</h2>
+
+<table  class="table table-bordered">
+    <thead>
+        <th style="width: 13">{%= report_columns[0].label %}</th>
+        <th style="width: {%= 100 - (report_columns.length - 1) * 13%}%">{%= report_columns[1].label %}</th>
+
+        {% for (let i=2; i<report_columns.length; i++) { %}
+            <th style="width: 13">{%= report_columns[i].label %}</th>
+        {% } %}
+    </thead>
+
+    <tbody>
+        {% for (let j=14; j<data.length; j++)  { %}
+        {%
+            var row = data[j];
+        %}
+        <tr >
+            {% for (let i=0; i<report_columns.length; i++) { %}
+                <td >
+                    {% const fieldname = report_columns[i].fieldname; %}
+                    {% if (!is_null(row[fieldname])) { %}
+                        {%= frappe.format(row[fieldname], report_columns[i], {}, row) %}
+                    {% } %}
+                </td>
+            {% } %}
+        </tr>
+        {% } %}
+    </tbody>
+
+</table>
\ No newline at end of file
diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.js b/erpnext/regional/report/uae_vat_201/uae_vat_201.js
new file mode 100644
index 0000000..5957424
--- /dev/null
+++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.js
@@ -0,0 +1,40 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["UAE VAT 201"] = {
+	"filters": [
+		{
+			"fieldname": "company",
+			"label": __("Company"),
+			"fieldtype": "Link",
+			"options": "Company",
+			"reqd": 1,
+			"default": frappe.defaults.get_user_default("Company")
+		},
+		{
+			"fieldname": "from_date",
+			"label": __("From Date"),
+			"fieldtype": "Date",
+			"reqd": 1,
+			"default": frappe.datetime.add_months(frappe.datetime.get_today(), -3),
+		},
+		{
+			"fieldname": "to_date",
+			"label": __("To Date"),
+			"fieldtype": "Date",
+			"reqd": 1,
+			"default": frappe.datetime.get_today()
+		},
+	],
+	"formatter": function(value, row, column, data, default_formatter) {
+		if (data
+			&& (data.legend=='VAT on Sales and All Other Outputs' || data.legend=='VAT on Expenses and All Other Inputs')
+			&& data.legend==value) {
+			value = $(`<span>${value}</span>`);
+			var $value = $(value).css("font-weight", "bold");
+			value = $value.wrap("<p></p>").parent().html();
+		}
+		return value;
+	},
+};
diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.json b/erpnext/regional/report/uae_vat_201/uae_vat_201.json
new file mode 100644
index 0000000..8a88bcd
--- /dev/null
+++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.json
@@ -0,0 +1,22 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2020-09-10 08:51:02.298482",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-09-10 08:51:02.298482",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "UAE VAT 201",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "GL Entry",
+ "report_name": "UAE VAT 201",
+ "report_type": "Script Report",
+ "roles": []
+}
\ No newline at end of file
diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py
new file mode 100644
index 0000000..b061423
--- /dev/null
+++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py
@@ -0,0 +1,339 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+
+def execute(filters=None):
+	columns = get_columns()
+	data, emirates, amounts_by_emirate = get_data(filters)
+	return columns, data
+
+def get_columns():
+	"""Creates a list of dictionaries that are used to generate column headers of the data table."""
+	return [
+		{
+			"fieldname": "no",
+			"label": _("No"),
+			"fieldtype": "Data",
+			"width": 50
+		},
+		{
+			"fieldname": "legend",
+			"label": _("Legend"),
+			"fieldtype": "Data",
+			"width": 300
+		},
+		{
+			"fieldname": "amount",
+			"label": _("Amount (AED)"),
+			"fieldtype": "Currency",
+			"width": 125,
+		},
+		{
+			"fieldname": "vat_amount",
+			"label": _("VAT Amount (AED)"),
+			"fieldtype": "Currency",
+			"width": 150,
+		}
+	]
+
+def get_data(filters = None):
+	"""Returns the list of dictionaries. Each dictionary is a row in the datatable and chart data."""
+	data = []
+	emirates, amounts_by_emirate = append_vat_on_sales(data, filters)
+	append_vat_on_expenses(data, filters)
+	return data, emirates, amounts_by_emirate
+
+def append_vat_on_sales(data, filters):
+	"""Appends Sales and All Other Outputs."""
+	append_data(data, '', _('VAT on Sales and All Other Outputs'), '', '')
+
+	emirates, amounts_by_emirate = standard_rated_expenses_emiratewise(data, filters)
+
+	append_data(data, '2',
+		 _('Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme'),
+		frappe.format((-1) * get_tourist_tax_return_total(filters), 'Currency'),
+		frappe.format((-1) * get_tourist_tax_return_tax(filters), 'Currency'))
+
+	append_data(data, '3', _('Supplies subject to the reverse charge provision'),
+		frappe.format(get_reverse_charge_total(filters), 'Currency'),
+		frappe.format(get_reverse_charge_tax(filters), 'Currency'))
+
+	append_data(data, '4', _('Zero Rated'),
+		frappe.format(get_zero_rated_total(filters), 'Currency'), "-")
+
+	append_data(data, '5', _('Exempt Supplies'),
+		frappe.format(get_exempt_total(filters), 'Currency'),"-")
+
+	append_data(data, '', '', '', '')
+
+	return emirates, amounts_by_emirate
+
+def standard_rated_expenses_emiratewise(data, filters):
+	"""Append emiratewise standard rated expenses and vat."""
+	total_emiratewise = get_total_emiratewise(filters)
+	emirates = get_emirates()
+	amounts_by_emirate = {}
+	for emirate, amount, vat in total_emiratewise:
+		amounts_by_emirate[emirate] = {
+			"legend": emirate,
+			"raw_amount": amount,
+			"raw_vat_amount": vat,
+			"amount": frappe.format(amount, 'Currency'),
+			"vat_amount": frappe.format(vat, 'Currency'),
+		}
+	amounts_by_emirate = append_emiratewise_expenses(data, emirates, amounts_by_emirate)
+	return emirates, amounts_by_emirate
+
+def append_emiratewise_expenses(data, emirates, amounts_by_emirate):
+	"""Append emiratewise standard rated expenses and vat."""
+	for no, emirate in enumerate(emirates, 97):
+		if emirate in amounts_by_emirate:
+			amounts_by_emirate[emirate]["no"] = _('1{0}').format(chr(no))
+			amounts_by_emirate[emirate]["legend"] = _('Standard rated supplies in {0}').format(emirate)
+			data.append(amounts_by_emirate[emirate])
+		else:
+			append_data(data, _('1{0}').format(chr(no)),
+				_('Standard rated supplies in {0}').format(emirate),
+				frappe.format(0, 'Currency'), frappe.format(0, 'Currency'))
+	return amounts_by_emirate
+
+def append_vat_on_expenses(data, filters):
+	"""Appends Expenses and All Other Inputs."""
+	append_data(data, '', _('VAT on Expenses and All Other Inputs'), '', '')
+	append_data(data, '9', _('Standard Rated Expenses'),
+		frappe.format(get_standard_rated_expenses_total(filters), 'Currency'),
+		frappe.format(get_standard_rated_expenses_tax(filters), 'Currency'))
+	append_data(data, '10', _('Supplies subject to the reverse charge provision'),
+		frappe.format(get_reverse_charge_recoverable_total(filters), 'Currency'),
+		frappe.format(get_reverse_charge_recoverable_tax(filters), 'Currency'))
+
+def append_data(data, no, legend, amount, vat_amount):
+	"""Returns data with appended value."""
+	data.append({"no": no, "legend":legend, "amount": amount, "vat_amount": vat_amount})
+
+def get_total_emiratewise(filters):
+	"""Returns Emiratewise Amount and Taxes."""
+	conditions = get_conditions(filters)
+	try:
+		return frappe.db.sql("""
+			select
+				s.vat_emirate as emirate, sum(i.base_amount) as total, sum(s.total_taxes_and_charges)
+			from
+				`tabSales Invoice Item` i inner join `tabSales Invoice` s
+			on
+				i.parent = s.name
+			where
+				s.docstatus = 1 and  i.is_exempt != 1 and i.is_zero_rated != 1
+				{where_conditions}
+			group by
+				s.vat_emirate;
+			""".format(where_conditions=conditions), filters)
+	except (IndexError, TypeError):
+		return 0
+
+def get_emirates():
+	"""Returns a List of emirates in the order that they are to be displayed."""
+	return [
+		'Abu Dhabi',
+		'Dubai',
+		'Sharjah',
+		'Ajman',
+		'Umm Al Quwain',
+		'Ras Al Khaimah',
+		'Fujairah'
+	]
+
+def get_filters(filters):
+	"""The conditions to be used to filter data to calculate the total sale."""
+	query_filters = []
+	if filters.get("company"):
+		query_filters.append(["company", '=', filters['company']])
+	if filters.get("from_date"):
+		query_filters.append(["posting_date", '>=', filters['from_date']])
+	if filters.get("from_date"):
+		query_filters.append(["posting_date", '<=', filters['to_date']])
+	return query_filters
+
+def get_reverse_charge_total(filters):
+	"""Returns the sum of the total of each Purchase invoice made."""
+	query_filters = get_filters(filters)
+	query_filters.append(['reverse_charge', '=', 'Y'])
+	query_filters.append(['docstatus', '=', 1])
+	try:
+		return frappe.db.get_all('Purchase Invoice',
+			filters = query_filters,
+			fields = ['sum(total)'],
+			as_list=True,
+			limit = 1
+		)[0][0] or 0
+	except (IndexError, TypeError):
+		return 0
+
+def get_reverse_charge_tax(filters):
+	"""Returns the sum of the tax of each Purchase invoice made."""
+	conditions = get_conditions_join(filters)
+	return frappe.db.sql("""
+		select sum(debit)  from
+			`tabPurchase Invoice` p inner join `tabGL Entry` gl
+		on
+			gl.voucher_no =  p.name
+		where
+			p.reverse_charge = "Y"
+			and p.docstatus = 1
+			and gl.docstatus = 1
+			and account in (select account from `tabUAE VAT Account` where  parent=%(company)s)
+			{where_conditions} ;
+		""".format(where_conditions=conditions), filters)[0][0] or 0
+
+def get_reverse_charge_recoverable_total(filters):
+	"""Returns the sum of the total of each Purchase invoice made with recoverable reverse charge."""
+	query_filters = get_filters(filters)
+	query_filters.append(['reverse_charge', '=', 'Y'])
+	query_filters.append(['recoverable_reverse_charge', '>', '0'])
+	query_filters.append(['docstatus', '=', 1])
+	try:
+		return frappe.db.get_all('Purchase Invoice',
+			filters = query_filters,
+			fields = ['sum(total)'],
+			as_list=True,
+			limit = 1
+		)[0][0] or 0
+	except (IndexError, TypeError):
+		return 0
+
+def get_reverse_charge_recoverable_tax(filters):
+	"""Returns the sum of the tax of each Purchase invoice made."""
+	conditions = get_conditions_join(filters)
+	return frappe.db.sql("""
+		select
+			sum(debit * p.recoverable_reverse_charge / 100)
+		from
+			`tabPurchase Invoice` p  inner join `tabGL Entry` gl
+		on
+			gl.voucher_no = p.name
+		where
+			p.reverse_charge = "Y"
+			and p.docstatus = 1
+			and p.recoverable_reverse_charge > 0
+			and gl.docstatus = 1
+			and account in (select account from `tabUAE VAT Account` where  parent=%(company)s)
+			{where_conditions} ;
+		""".format(where_conditions=conditions), filters)[0][0] or 0
+
+def get_conditions_join(filters):
+	"""The conditions to be used to filter data to calculate the total vat."""
+	conditions = ""
+	for opts in (("company", " and p.company=%(company)s"),
+		("from_date", " and p.posting_date>=%(from_date)s"),
+		("to_date", " and p.posting_date<=%(to_date)s")):
+		if filters.get(opts[0]):
+			conditions += opts[1]
+	return conditions
+
+def get_standard_rated_expenses_total(filters):
+	"""Returns the sum of the total of each Purchase invoice made with recoverable reverse charge."""
+	query_filters = get_filters(filters)
+	query_filters.append(['recoverable_standard_rated_expenses', '>', 0])
+	query_filters.append(['docstatus', '=', 1])
+	try:
+		return frappe.db.get_all('Purchase Invoice',
+			filters = query_filters,
+			fields = ['sum(total)'],
+			as_list=True,
+			limit = 1
+		)[0][0]  or 0
+	except (IndexError, TypeError):
+		return 0
+
+def get_standard_rated_expenses_tax(filters):
+	"""Returns the sum of the tax of each Purchase invoice made."""
+	query_filters = get_filters(filters)
+	query_filters.append(['recoverable_standard_rated_expenses', '>', 0])
+	query_filters.append(['docstatus', '=', 1])
+	try:
+		return frappe.db.get_all('Purchase Invoice',
+			filters = query_filters,
+			fields = ['sum(recoverable_standard_rated_expenses)'],
+			as_list=True,
+			limit = 1
+		)[0][0]  or 0
+	except (IndexError, TypeError):
+		return 0
+
+def get_tourist_tax_return_total(filters):
+	"""Returns the sum of the total of each Sales invoice with non zero tourist_tax_return."""
+	query_filters = get_filters(filters)
+	query_filters.append(['tourist_tax_return', '>', 0])
+	query_filters.append(['docstatus', '=', 1])
+	try:
+		return frappe.db.get_all('Sales Invoice',
+			filters = query_filters,
+			fields = ['sum(total)'],
+			as_list=True,
+			limit = 1
+		)[0][0]  or 0
+	except (IndexError, TypeError):
+		return 0
+
+def get_tourist_tax_return_tax(filters):
+	"""Returns the sum of the tax of each Sales invoice with non zero tourist_tax_return."""
+	query_filters = get_filters(filters)
+	query_filters.append(['tourist_tax_return', '>', 0])
+	query_filters.append(['docstatus', '=', 1])
+	try:
+		return frappe.db.get_all('Sales Invoice',
+			filters = query_filters,
+			fields = ['sum(tourist_tax_return)'],
+			as_list=True,
+			limit = 1
+		)[0][0]  or 0
+	except (IndexError, TypeError):
+		return 0
+
+def get_zero_rated_total(filters):
+	"""Returns the sum of each Sales Invoice Item Amount which is zero rated."""
+	conditions = get_conditions(filters)
+	try:
+		return frappe.db.sql("""
+			select
+				sum(i.base_amount) as total
+			from
+				`tabSales Invoice Item` i inner join `tabSales Invoice` s
+			on
+				i.parent = s.name
+			where
+				s.docstatus = 1 and  i.is_zero_rated = 1
+				{where_conditions} ;
+			""".format(where_conditions=conditions), filters)[0][0] or 0
+	except (IndexError, TypeError):
+		return 0
+
+def get_exempt_total(filters):
+	"""Returns the sum of each Sales Invoice Item Amount which is Vat Exempt."""
+	conditions = get_conditions(filters)
+	try:
+		return frappe.db.sql("""
+			select
+				sum(i.base_amount) as total
+			from
+				`tabSales Invoice Item` i inner join `tabSales Invoice` s
+			on
+				i.parent = s.name
+			where
+				s.docstatus = 1 and  i.is_exempt = 1
+				{where_conditions} ;
+			""".format(where_conditions=conditions), filters)[0][0] or 0
+	except (IndexError, TypeError):
+		return 0
+def get_conditions(filters):
+	"""The conditions to be used to filter data to calculate the total sale."""
+	conditions = ""
+	for opts in (("company", " and company=%(company)s"),
+		("from_date", " and posting_date>=%(from_date)s"),
+		("to_date", " and posting_date<=%(to_date)s")):
+		if filters.get(opts[0]):
+			conditions += opts[1]
+	return conditions
diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py
index 250659e..013ae5c 100644
--- a/erpnext/regional/united_arab_emirates/setup.py
+++ b/erpnext/regional/united_arab_emirates/setup.py
@@ -5,24 +5,30 @@
 
 import frappe, os, json
 from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+from frappe.permissions import add_permission, update_permission_property
 from erpnext.setup.setup_wizard.operations.taxes_setup import create_sales_tax
 
 def setup(company=None, patch=True):
 	make_custom_fields()
 	add_print_formats()
-
+	add_custom_roles_for_reports()
+	add_permissions()
 	if company:
 		create_sales_tax(company)
 
 def make_custom_fields():
+	is_zero_rated = dict(fieldname='is_zero_rated', label='Is Zero Rated',
+		fieldtype='Check', fetch_from='item_code.is_zero_rated', insert_after='description',
+		print_hide=1)
+	is_exempt = dict(fieldname='is_exempt', label='Is Exempt',
+		fieldtype='Check', fetch_from='item_code.is_exempt', insert_after='is_zero_rated',
+		print_hide=1)
+
 	invoice_fields = [
 		dict(fieldname='vat_section', label='VAT Details', fieldtype='Section Break',
 			insert_after='group_same_items', print_hide=1, collapsible=1),
 		dict(fieldname='permit_no', label='Permit Number',
 			fieldtype='Data', insert_after='vat_section', print_hide=1),
-		dict(fieldname='reverse_charge_applicable', label='Reverse Charge Applicable',
-			fieldtype='Select', insert_after='permit_no', print_hide=1,
-			options='Y\nN', default='N')
 	]
 
 	purchase_invoice_fields = [
@@ -31,7 +37,16 @@
 				fetch_from='company.tax_id', print_hide=1),
 			dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic',
 				fieldtype='Read Only', insert_after='supplier_name',
-				fetch_from='supplier.supplier_name_in_arabic', print_hide=1)
+				fetch_from='supplier.supplier_name_in_arabic', print_hide=1),
+			dict(fieldname='recoverable_standard_rated_expenses', print_hide=1, default='0',
+				label='Recoverable Standard Rated Expenses (AED)', insert_after='permit_no',
+				fieldtype='Currency', ),
+			dict(fieldname='reverse_charge', label='Reverse Charge Applicable',
+				fieldtype='Select', insert_after='recoverable_standard_rated_expenses', print_hide=1,
+				options='Y\nN', default='N'),
+			dict(fieldname='recoverable_reverse_charge', label='Recoverable Reverse Charge (Percentage)',
+				insert_after='reverse_charge', fieldtype='Percent', print_hide=1,
+				depends_on="eval:doc.reverse_charge=='Y'", default='100.000'),
 		]
 
 	sales_invoice_fields = [
@@ -41,6 +56,11 @@
 			dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic',
 				fieldtype='Read Only', insert_after='customer_name',
 				fetch_from='customer.customer_name_in_arabic', print_hide=1),
+			dict(fieldname='vat_emirate', label='VAT Emirate', insert_after='permit_no', fieldtype='Select',
+				options='\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain',
+				fetch_from='company_address.emirate'),
+			dict(fieldname='tourist_tax_return', label='Tax Refund provided to Tourists (AED)',
+				insert_after='vat_emirate', fieldtype='Currency', print_hide=1, default='0'),
 		]
 
 	invoice_item_fields = [
@@ -67,6 +87,12 @@
 		'Item': [
 			dict(fieldname='tax_code', label='Tax Code',
 				fieldtype='Data', insert_after='item_group'),
+			dict(fieldname='is_zero_rated', label='Is Zero Rated',
+				fieldtype='Check', insert_after='tax_code',
+				print_hide=1),
+			dict(fieldname='is_exempt', label='Is Exempt',
+				fieldtype='Check', insert_after='is_zero_rated',
+				print_hide=1)
 		],
 		'Customer': [
 			dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic',
@@ -76,13 +102,17 @@
 			dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic',
 				fieldtype='Data', insert_after='supplier_name'),
 		],
+		'Address': [
+			dict(fieldname='emirate', label='Emirate', fieldtype='Select', insert_after='state',
+			options='\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain')
+		],
 		'Purchase Invoice': purchase_invoice_fields + invoice_fields,
 		'Purchase Order': purchase_invoice_fields + invoice_fields,
 		'Purchase Receipt': purchase_invoice_fields + invoice_fields,
 		'Sales Invoice': sales_invoice_fields + invoice_fields,
 		'Sales Order': sales_invoice_fields + invoice_fields,
 		'Delivery Note': sales_invoice_fields + invoice_fields,
-		'Sales Invoice Item': invoice_item_fields + delivery_date_field,
+		'Sales Invoice Item': invoice_item_fields + delivery_date_field + [is_zero_rated, is_exempt],
 		'Purchase Invoice Item': invoice_item_fields,
 		'Sales Order Item': invoice_item_fields,
 		'Delivery Note Item': invoice_item_fields,
@@ -101,3 +131,25 @@
 
 	frappe.db.sql(""" update `tabPrint Format` set disabled = 0 where
 		name in('Simplified Tax Invoice', 'Detailed Tax Invoice', 'Tax Invoice') """)
+
+def add_custom_roles_for_reports():
+	"""Add Access Control to UAE VAT 201."""
+	if not frappe.db.get_value('Custom Role', dict(report='UAE VAT 201')):
+		frappe.get_doc(dict(
+			doctype='Custom Role',
+			report='UAE VAT 201',
+			roles= [
+				dict(role='Accounts User'),
+				dict(role='Accounts Manager'),
+				dict(role='Auditor')
+			]
+		)).insert()
+
+def add_permissions():
+	"""Add Permissions for UAE VAT Settings and UAE VAT Account."""
+	for doctype in ('UAE VAT Settings', 'UAE VAT Account'):
+		add_permission(doctype, 'All', 0)
+		for role in ('Accounts Manager', 'Accounts User', 'System Manager'):
+			add_permission(doctype, role, 0)
+			update_permission_property(doctype, role, 0, 'write', 1)
+			update_permission_property(doctype, role, 0, 'create', 1)
diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py
index a0425f6..7d5fd6e 100644
--- a/erpnext/regional/united_arab_emirates/utils.py
+++ b/erpnext/regional/united_arab_emirates/utils.py
@@ -1,6 +1,8 @@
 from __future__ import unicode_literals
 import frappe
-from frappe.utils import flt
+from frappe import _
+import erpnext
+from frappe.utils import flt, round_based_on_smallest_currency_fraction, money_in_words
 from erpnext.controllers.taxes_and_totals import get_itemised_tax
 from six import iteritems
 
@@ -26,4 +28,134 @@
 
 		row.tax_rate = flt(tax_rate, row.precision("tax_rate"))
 		row.tax_amount = flt((row.net_amount * tax_rate) / 100, row.precision("net_amount"))
-		row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount"))
\ No newline at end of file
+		row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount"))
+
+def get_account_currency(account):
+	"""Helper function to get account currency."""
+	if not account:
+		return
+	def generator():
+		account_currency, company = frappe.get_cached_value(
+			"Account",
+			account,
+			["account_currency",
+			"company"]
+		)
+		if not account_currency:
+			account_currency = frappe.get_cached_value('Company',  company,  "default_currency")
+
+		return account_currency
+
+	return frappe.local_cache("account_currency", account, generator)
+
+def get_tax_accounts(company):
+	"""Get the list of tax accounts for a specific company."""
+	tax_accounts_dict = frappe._dict()
+	tax_accounts_list = frappe.get_all("UAE VAT Account",
+		filters={"parent": company},
+		fields=["Account"]
+		)
+
+	if not tax_accounts_list and not frappe.flags.in_test:
+		frappe.throw(_('Please set Vat Accounts for Company: "{0}" in UAE VAT Settings').format(company))
+	for tax_account in tax_accounts_list:
+		for account, name in tax_account.items():
+			tax_accounts_dict[name] = name
+
+	return tax_accounts_dict
+
+def update_grand_total_for_rcm(doc, method):
+	"""If the Reverse Charge is Applicable subtract the tax amount from the grand total and update in the form."""
+	country = frappe.get_cached_value('Company', doc.company, 'country')
+
+	if country != 'United Arab Emirates':
+		return
+
+	if not doc.total_taxes_and_charges:
+		return
+
+	if doc.reverse_charge == 'Y':
+		tax_accounts = get_tax_accounts(doc.company)
+
+		base_vat_tax = 0
+		vat_tax = 0
+
+		for tax in doc.get('taxes'):
+			if tax.category not in ("Total", "Valuation and Total"):
+				continue
+
+			if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in tax_accounts:
+				base_vat_tax += tax.base_tax_amount_after_discount_amount
+				vat_tax += tax.tax_amount_after_discount_amount
+
+		doc.taxes_and_charges_added -= vat_tax
+		doc.total_taxes_and_charges -= vat_tax
+		doc.base_taxes_and_charges_added -= base_vat_tax
+		doc.base_total_taxes_and_charges -= base_vat_tax
+
+		update_totals(vat_tax, base_vat_tax, doc)
+
+def update_totals(vat_tax, base_vat_tax, doc):
+	"""Update the grand total values in the form."""
+	doc.base_grand_total -= base_vat_tax
+	doc.grand_total -= vat_tax
+
+	if doc.meta.get_field("rounded_total"):
+
+		if doc.is_rounded_total_disabled():
+			doc.outstanding_amount = doc.grand_total
+
+		else:
+			doc.rounded_total = round_based_on_smallest_currency_fraction(doc.grand_total,
+				doc.currency, doc.precision("rounded_total"))
+			doc.rounding_adjustment = flt(doc.rounded_total - doc.grand_total,
+				doc.precision("rounding_adjustment"))
+			doc.outstanding_amount = doc.rounded_total or doc.grand_total
+
+	doc.in_words = money_in_words(doc.grand_total, doc.currency)
+	doc.base_in_words = money_in_words(doc.base_grand_total, erpnext.get_company_currency(doc.company))
+	doc.set_payment_schedule()
+
+def make_regional_gl_entries(gl_entries, doc):
+	"""Hooked to make_regional_gl_entries in Purchase Invoice.It appends the region specific general ledger entries to the list of GL Entries."""
+	country = frappe.get_cached_value('Company', doc.company, 'country')
+
+	if country != 'United Arab Emirates':
+		return gl_entries
+
+	if doc.reverse_charge == 'Y':
+		tax_accounts = get_tax_accounts(doc.company)
+		for tax in doc.get('taxes'):
+			if tax.category not in ("Total", "Valuation and Total"):
+				continue
+			gl_entries = make_gl_entry(tax, gl_entries, doc, tax_accounts)
+	return gl_entries
+
+def make_gl_entry(tax, gl_entries, doc, tax_accounts):
+	dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit"
+	if flt(tax.base_tax_amount_after_discount_amount)  and tax.account_head in tax_accounts:
+		account_currency = get_account_currency(tax.account_head)
+
+		gl_entries.append(doc.get_gl_dict({
+				"account": tax.account_head,
+				"cost_center": tax.cost_center,
+				"posting_date": doc.posting_date,
+				"against": doc.supplier,
+				dr_or_cr: tax.base_tax_amount_after_discount_amount,
+				dr_or_cr + "_in_account_currency": tax.base_tax_amount_after_discount_amount \
+					if account_currency==doc.company_currency \
+					else tax.tax_amount_after_discount_amount
+			}, account_currency, item=tax
+		))
+	return gl_entries
+
+
+def validate_returns(doc, method):
+	"""Standard Rated expenses should not be set when Reverse Charge Applicable is set."""
+	country = frappe.get_cached_value('Company', doc.company, 'country')
+	if country != 'United Arab Emirates':
+		return
+	if doc.reverse_charge == 'Y' and  flt(doc.recoverable_standard_rated_expenses) != 0:
+		frappe.throw(_(
+			"Recoverable Standard Rated expenses should not be set when Reverse Charge Applicable is Y"
+		))
diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json
index 5b85187..3eba62b 100644
--- a/erpnext/selling/doctype/quotation/quotation.json
+++ b/erpnext/selling/doctype/quotation/quotation.json
@@ -1,5 +1,6 @@
 {
  "actions": [],
+ "allow_auto_repeat": 1,
  "allow_import": 1,
  "autoname": "naming_series:",
  "creation": "2013-05-24 19:29:08",
@@ -932,7 +933,7 @@
  "is_submittable": 1,
  "links": [],
  "max_attachments": 1,
- "modified": "2020-07-26 17:46:19.951223",
+ "modified": "2020-10-30 13:58:59.212060",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Quotation",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index 77c1787..3d64ac3 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -1,5 +1,6 @@
 {
  "actions": [],
+ "allow_auto_repeat": 1,
  "allow_import": 1,
  "autoname": "naming_series:",
  "creation": "2013-06-18 12:39:59",
@@ -1460,7 +1461,7 @@
  "idx": 105,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-10-07 14:30:01.782617",
+ "modified": "2020-10-30 13:59:18.628077",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index ec1c823..04d85e5 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -844,7 +844,8 @@
 						"contact_email",
 						"contact_person",
 						"taxes_and_charges",
-						"shipping_address"
+						"shipping_address",
+						"terms"
 					],
 					"validation": {
 						"docstatus": ["=", 1]
@@ -863,7 +864,10 @@
 					"field_no_map": [
 						"rate",
 						"price_list_rate",
-						"item_tax_template"
+						"item_tax_template",
+						"discount_percentage",
+						"discount_amount",
+						"pricing_rules"
 					],
 					"postprocess": update_item,
 					"condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.supplier == supplier and doc.item_code in items_to_map
@@ -917,7 +921,8 @@
 				"contact_email",
 				"contact_person",
 				"taxes_and_charges",
-				"shipping_address"
+				"shipping_address",
+				"terms"
 			],
 			"validation": {
 				"docstatus": ["=", 1]
@@ -937,7 +942,10 @@
 				"rate",
 				"price_list_rate",
 				"item_tax_template",
-				"supplier"
+				"discount_percentage",
+				"discount_amount",
+				"supplier",
+				"pricing_rules"
 			],
 			"postprocess": update_item,
 			"condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.item_code in items_to_map
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py
index a690050..062cba1 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.py
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.py
@@ -62,6 +62,7 @@
 			`tabItem` item {bin_join_selection}
 		WHERE
 			item.disabled = 0
+			AND item.is_stock_item = 1
 			AND item.has_variants = 0
 			AND item.is_sales_item = 1
 			AND item.is_fixed_asset = 0
diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py
index b30bd78..cbb4c7c 100644
--- a/erpnext/setup/doctype/email_digest/email_digest.py
+++ b/erpnext/setup/doctype/email_digest/email_digest.py
@@ -48,12 +48,8 @@
 		recipients = list(filter(lambda r: r in valid_users,
 			self.recipient_list.split("\n")))
 
-		original_user = frappe.session.user
-
 		if recipients:
 			for user_id in recipients:
-				frappe.set_user(user_id)
-				frappe.set_user_lang(user_id)
 				msg_for_this_recipient = self.get_msg_html()
 				if msg_for_this_recipient:
 					frappe.sendmail(
@@ -64,9 +60,6 @@
 						reference_name = self.name,
 						unsubscribe_message = _("Unsubscribe from this Email Digest"))
 
-		frappe.set_user(original_user)
-		frappe.set_user_lang(original_user)
-
 	def get_msg_html(self):
 		"""Build email digest content"""
 		frappe.flags.ignore_account_permission = True
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index 3c5129b..7393c8a 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -1,5 +1,6 @@
 {
  "actions": [],
+ "allow_auto_repeat": 1,
  "allow_import": 1,
  "autoname": "naming_series:",
  "creation": "2013-05-24 19:29:09",
diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py
index 51b47c5..bed5ea9 100644
--- a/erpnext/stock/doctype/item_price/item_price.py
+++ b/erpnext/stock/doctype/item_price/item_price.py
@@ -4,14 +4,13 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
-
-
-class ItemPriceDuplicateItem(frappe.ValidationError): pass
-
-
 from frappe.model.document import Document
 
 
+class ItemPriceDuplicateItem(frappe.ValidationError):
+	pass
+
+
 class ItemPrice(Document):
 
 	def validate(self):
@@ -23,7 +22,7 @@
 
 	def validate_item(self):
 		if not frappe.db.exists("Item", self.item_code):
-			frappe.throw(_("Item {0} not found").format(self.item_code))
+			frappe.throw(_("Item {0} not found.").format(self.item_code))
 
 	def validate_dates(self):
 		if self.valid_from and self.valid_upto:
@@ -38,40 +37,45 @@
 
 			if not price_list_details:
 				link = frappe.utils.get_link_to_form('Price List', self.price_list)
-				frappe.throw("The price list {0} does not exists or disabled".
-					format(link))
+				frappe.throw("The price list {0} does not exist or is disabled".format(link))
 
 			self.buying, self.selling, self.currency = price_list_details
 
 	def update_item_details(self):
 		if self.item_code:
-			self.item_name, self.item_description = frappe.db.get_value("Item",
-				self.item_code,["item_name", "description"])
+			self.item_name, self.item_description = frappe.db.get_value("Item", self.item_code,["item_name", "description"])
 
 	def check_duplicates(self):
-		conditions = "where item_code=%(item_code)s and price_list=%(price_list)s and name != %(name)s"
-		condition_data_dict = dict(item_code=self.item_code, price_list=self.price_list, name=self.name)
+		conditions = """where item_code = %(item_code)s and price_list = %(price_list)s and name != %(name)s"""
 
-		for field in ['uom', 'valid_from',
-			'valid_upto', 'packing_unit', 'customer', 'supplier']:
+		for field in [
+			"uom",
+			"valid_from",
+			"valid_upto",
+			"packing_unit",
+			"customer",
+			"supplier",]:
 			if self.get(field):
-				conditions += " and {0} = %({1})s".format(field, field)
-				condition_data_dict[field] = self.get(field)
+				conditions += " and {0} = %({0})s ".format(field)
+			else:
+				conditions += "and (isnull({0}) or {0} = '')".format(field)
 
 		price_list_rate = frappe.db.sql("""
-			SELECT price_list_rate
-			FROM `tabItem Price`
-			  {conditions} """.format(conditions=conditions), condition_data_dict)
+				select price_list_rate
+				from `tabItem Price`
+				{conditions}
+			""".format(conditions=conditions),
+			self.as_dict(),)
 
-		if price_list_rate :
-			frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty and Dates."), ItemPriceDuplicateItem)
+		if price_list_rate:
+			frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty, and Dates."), ItemPriceDuplicateItem,)
 
 	def before_save(self):
 		if self.selling:
 			self.reference = self.customer
 		if self.buying:
 			self.reference = self.supplier
-		
+
 		if self.selling and not self.buying:
 			# if only selling then remove supplier
 			self.supplier = None
diff --git a/erpnext/stock/doctype/item_price/test_item_price.py b/erpnext/stock/doctype/item_price/test_item_price.py
index 702acc3..f3d406e 100644
--- a/erpnext/stock/doctype/item_price/test_item_price.py
+++ b/erpnext/stock/doctype/item_price/test_item_price.py
@@ -138,4 +138,23 @@
 		# Valid price list must already exist
 		self.assertRaises(frappe.ValidationError, doc.save)
 
+	def test_empty_duplicate_validation(self):
+		# Check if none/empty values are not compared during insert validation
+		doc = frappe.copy_doc(test_records[2])
+		doc.customer = None
+		doc.price_list_rate = 21
+		doc.insert()
+
+		args = {
+			"price_list": doc.price_list,
+			"uom": "_Test UOM",
+			"transaction_date": '2017-04-18',
+			"qty": 7
+		}
+
+		price = get_price_list_rate_for(args, doc.item_code)
+		frappe.db.rollback()
+
+		self.assertEqual(price, 21)
+
 test_records = frappe.get_test_records('Item Price')
diff --git a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
index f1e1fd3..888bc2d 100644
--- a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
+++ b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json
@@ -1,88 +1,57 @@
 {
- "allow_copy": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "hash", 
- "beta": 0, 
- "creation": "2013-02-22 01:28:01", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "editable_grid": 1, 
+ "actions": [],
+ "autoname": "hash",
+ "creation": "2013-02-22 01:28:01",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "specification",
+  "value",
+  "column_break_3",
+  "acceptance_formula"
+ ],
  "fields": [
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "fieldname": "specification", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Parameter", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "specification", 
-   "oldfieldtype": "Data", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "200px", 
-   "read_only": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0, 
+   "fieldname": "specification",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Parameter",
+   "oldfieldname": "specification",
+   "oldfieldtype": "Data",
+   "print_width": "200px",
+   "reqd": 1,
    "width": "200px"
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "fieldname": "value", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Acceptance Criteria", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "value", 
-   "oldfieldtype": "Data", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
+   "fieldname": "value",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Acceptance Criteria",
+   "oldfieldname": "value",
+   "oldfieldtype": "Data"
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "description": "Simple Python formula based on numeric Readings.<br> Example 1: <b>reading_1 &gt; 0.2 and reading_1 &lt; 0.5</b><br>\nExample 2: <b>(reading_1 + reading_2) / 2 &lt; 10</b>",
+   "fieldname": "acceptance_formula",
+   "fieldtype": "Code",
+   "in_list_view": 1,
+   "label": "Acceptance Criteria Formula"
   }
- ], 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 1, 
- "image_view": 0, 
- "in_create": 0, 
-
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2016-07-11 03:28:01.074316", 
- "modified_by": "Administrator", 
- "module": "Stock", 
- "name": "Item Quality Inspection Parameter", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "track_seen": 0
+ ],
+ "idx": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2020-11-16 16:33:42.421842",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Item Quality Inspection Parameter",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC"
 }
\ No newline at end of file
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index ce54fc8..13c8ceb 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -1,5 +1,6 @@
 {
  "actions": [],
+ "allow_auto_repeat": 1,
  "allow_import": 1,
  "autoname": "naming_series:",
  "creation": "2013-05-21 16:16:39",
@@ -1109,7 +1110,7 @@
  "idx": 261,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-08-03 23:20:26.381024",
+ "modified": "2020-10-30 14:00:08.347534",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt",
diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py
index c3bb514..399a63a 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py
@@ -4,15 +4,20 @@
 from __future__ import unicode_literals
 import frappe
 from frappe.model.document import Document
+from frappe.model.mapper import get_mapped_doc
+from frappe import _
+from frappe.utils import flt
 from erpnext.stock.doctype.quality_inspection_template.quality_inspection_template \
 	import get_template_details
-from frappe.model.mapper import get_mapped_doc
 
 class QualityInspection(Document):
 	def validate(self):
 		if not self.readings and self.item_code:
 			self.get_item_specification_details()
 
+		if self.readings:
+			self.set_status_based_on_acceptance_formula()
+
 	def get_item_specification_details(self):
 		if not self.quality_inspection_template:
 			self.quality_inspection_template = frappe.db.get_value('Item',
@@ -26,6 +31,7 @@
 			child = self.append('readings', {})
 			child.specification = d.specification
 			child.value = d.value
+			child.acceptance_formula = d.acceptance_formula
 			child.status = "Accepted"
 
 	def get_quality_inspection_template(self):
@@ -58,6 +64,29 @@
 				.format(parent_doc=self.reference_type, child_doc=doctype),
 				(quality_inspection, self.modified, self.reference_name, self.item_code))
 
+	def set_status_based_on_acceptance_formula(self):
+		for reading in self.readings:
+			if not reading.acceptance_formula: continue
+
+			condition = reading.acceptance_formula
+			data = {}
+			for i in range(1, 11):
+				field = "reading_" + str(i)
+				data[field] = flt(reading.get(field)) or 0
+
+			try:
+				result = frappe.safe_eval(condition, None, data)
+				reading.status = "Accepted" if result else "Rejected"
+			except SyntaxError:
+				frappe.throw(_("Row #{0}: Acceptance Criteria Formula is incorrect.").format(reading.idx),
+					title=_("Invalid Formula"))
+			except NameError as e:
+				field = frappe.bold(e.args[0].split()[1])
+				frappe.throw(_("Row #{0}: {1} is not a valid reading field. Please refer to the field description.")
+					.format(reading.idx, field),
+					title=_("Invalid Formula"))
+
+
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
 def item_query(doctype, txt, searchfield, start, page_len, filters):
diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
index bb535c1..2c40009 100644
--- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
@@ -7,6 +7,7 @@
 from frappe.utils import nowdate
 from erpnext.stock.doctype.item.test_item import create_item
 from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 from erpnext.controllers.stock_controller import QualityInspectionRejectedError, QualityInspectionRequiredError, QualityInspectionNotSubmittedError
 
 # test_records = frappe.get_test_records('Quality Inspection')
@@ -17,10 +18,12 @@
 		frappe.db.set_value("Item", "_Test Item with QA", "inspection_required_before_delivery", 1)
 
 	def test_qa_for_delivery(self):
+		make_stock_entry(item_code="_Test Item with QA", target="_Test Warehouse - _TC", qty=1, basic_rate=100)
 		dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
+
 		self.assertRaises(QualityInspectionRequiredError, dn.submit)
 
-		qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, status="Rejected", submit=True)
+		qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, status="Rejected")
 		dn.reload()
 		self.assertRaises(QualityInspectionRejectedError, dn.submit)
 
@@ -28,12 +31,51 @@
 		dn.reload()
 		dn.submit()
 
+		qa.cancel()
+		dn.reload()
+		dn.cancel()
+
 	def test_qa_not_submit(self):
 		dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
-		qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, submit = False)
+		qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, do_not_submit=True)
 		dn.items[0].quality_inspection = qa.name
 		self.assertRaises(QualityInspectionNotSubmittedError, dn.submit)
 
+		qa.delete()
+		dn.delete()
+
+	def test_formula_based_qi_readings(self):
+		dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
+		readings = [{
+			"specification": "Iron Content",
+			"acceptance_formula": "reading_1 > 0.35 and reading_1 < 0.50",
+			"reading_1": 0.4
+		},
+		{
+			"specification": "Calcium Content",
+			"acceptance_formula": "reading_1 > 0.20 and reading_1 < 0.50",
+			"reading_1": 0.7
+		},
+		{
+			"specification": "Mg Content",
+			"acceptance_formula": "(reading_1 + reading_2 + reading_3) / 3 < 0.9",
+			"reading_1": 0.5,
+			"reading_2": 0.7,
+			"reading_3": "random text" # check if random string input causes issues
+		}]
+
+		qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name,
+			readings=readings, do_not_save=True)
+		qa.save()
+
+		# status must be auto set as per formula
+		self.assertEqual(qa.readings[0].status, "Accepted")
+		self.assertEqual(qa.readings[1].status, "Rejected")
+		self.assertEqual(qa.readings[2].status, "Accepted")
+
+		qa.delete()
+		dn.delete()
+
 def create_quality_inspection(**args):
 	args = frappe._dict(args)
 	qa = frappe.new_doc("Quality Inspection")
@@ -44,12 +86,18 @@
 	qa.item_code = args.item_code or "_Test Item with QA"
 	qa.sample_size = 1
 	qa.inspected_by = frappe.session.user
-	qa.append("readings", {
-		"specification": "Size",
-		"status": args.status
-	})
-	qa.save()
-	if args.submit:
-		qa.submit()
+
+	readings = args.readings or {"specification": "Size", "status": args.status}
+
+	if isinstance(readings, list):
+		for entry in readings:
+			qa.append("readings", entry)
+	else:
+		qa.append("readings", readings)
+
+	if not args.do_not_save:
+		qa.save()
+		if not args.do_not_submit:
+			qa.submit()
 
 	return qa
diff --git a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json
index f9f8a71..c1976dd 100644
--- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json
+++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json
@@ -1,22 +1,29 @@
 {
+ "actions": [],
  "autoname": "hash",
  "creation": "2013-02-22 01:27:43",
  "doctype": "DocType",
  "editable_grid": 1,
+ "engine": "InnoDB",
  "field_order": [
   "specification",
   "value",
+  "status",
+  "column_break_4",
+  "acceptance_formula",
+  "section_break_3",
   "reading_1",
   "reading_2",
   "reading_3",
+  "column_break_10",
   "reading_4",
   "reading_5",
   "reading_6",
+  "column_break_14",
   "reading_7",
   "reading_8",
   "reading_9",
-  "reading_10",
-  "status"
+  "reading_10"
  ],
  "fields": [
   {
@@ -124,15 +131,40 @@
    "oldfieldname": "status",
    "oldfieldtype": "Select",
    "options": "Accepted\nRejected"
+  },
+  {
+   "fieldname": "section_break_3",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  },
+  {
+   "description": "Simple Python formula based on numeric Readings.<br> Example 1: <b>reading_1 &gt; 0.2 and reading_1 &lt; 0.5</b><br>\nExample 2: <b>(reading_1 + reading_2) / 2 &lt; 10</b>",
+   "fieldname": "acceptance_formula",
+   "fieldtype": "Code",
+   "label": "Acceptance Criteria Formula"
+  },
+  {
+   "fieldname": "column_break_10",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "column_break_14",
+   "fieldtype": "Column Break"
   }
  ],
  "idx": 1,
  "istable": 1,
- "modified": "2019-07-11 18:48:12.667404",
+ "links": [],
+ "modified": "2020-11-16 16:34:29.947856",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Quality Inspection Reading",
  "owner": "Administrator",
  "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py
index 0d9a903..e284846 100644
--- a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py
+++ b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py
@@ -12,5 +12,7 @@
 def get_template_details(template):
 	if not template: return []
 
-	return frappe.get_all('Item Quality Inspection Parameter', fields=["specification", "value"],
-		filters={'parenttype': 'Quality Inspection Template', 'parent': template}, order_by="idx")
\ No newline at end of file
+	return frappe.get_all('Item Quality Inspection Parameter',
+		fields=["specification", "value", "acceptance_formula"],
+		filters={'parenttype': 'Quality Inspection Template', 'parent': template},
+		order_by="idx")
\ No newline at end of file
diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py
index 3dc806f..8aaf7ab 100644
--- a/erpnext/stock/report/stock_ageing/stock_ageing.py
+++ b/erpnext/stock/report/stock_ageing/stock_ageing.py
@@ -20,7 +20,8 @@
 
 		fifo_queue = sorted(filter(_func, item_dict["fifo_queue"]), key=_func)
 		details = item_dict["details"]
-		if not fifo_queue and (not item_dict.get("total_qty")): continue
+
+		if not fifo_queue: continue
 
 		average_age = get_average_age(fifo_queue, to_date)
 		earliest_age = date_diff(to_date, fifo_queue[0][1])