Merge branch 'develop' into values-out-of-sync-jv
diff --git a/.github/workflows/server-tests-mariadb.yml b/.github/workflows/server-tests-mariadb.yml
index 8cc5826..cdb6849 100644
--- a/.github/workflows/server-tests-mariadb.yml
+++ b/.github/workflows/server-tests-mariadb.yml
@@ -129,6 +129,9 @@
     needs: test
     runs-on: ubuntu-latest
     steps:
+      - name: Clone
+        uses: actions/checkout@v2
+
       - name: Download artifacts
         uses: actions/download-artifact@v3
 
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index 897151a..4453783 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -205,10 +205,16 @@
 	return frappe.get_hooks("accounting_dimension_doctypes")
 
 
-def get_accounting_dimensions(as_list=True):
+def get_accounting_dimensions(as_list=True, filters=None):
+
+	if not filters:
+		filters = {"disabled": 0}
+
 	if frappe.flags.accounting_dimensions is None:
 		frappe.flags.accounting_dimensions = frappe.get_all(
-			"Accounting Dimension", fields=["label", "fieldname", "disabled", "document_type"]
+			"Accounting Dimension",
+			fields=["label", "fieldname", "disabled", "document_type"],
+			filters=filters,
 		)
 
 	if as_list:
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index 907b769..b596df9 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -350,9 +350,13 @@
 			)
 
 			if self.minimum_invoice_amount:
-				condition += " and `{0}` >= {1}".format(dr_or_cr, flt(self.minimum_invoice_amount))
+				condition += " and {dr_or_cr} >= {amount}".format(
+					dr_or_cr=dr_or_cr, amount=flt(self.minimum_invoice_amount)
+				)
 			if self.maximum_invoice_amount:
-				condition += " and `{0}` <= {1}".format(dr_or_cr, flt(self.maximum_invoice_amount))
+				condition += " and {dr_or_cr} <= {amount}".format(
+					dr_or_cr=dr_or_cr, amount=flt(self.maximum_invoice_amount)
+				)
 
 		elif get_return_invoices:
 			condition = " and doc.company = '{0}' ".format(self.company)
@@ -367,15 +371,19 @@
 				else ""
 			)
 			dr_or_cr = (
-				"gl.debit_in_account_currency"
+				"debit_in_account_currency"
 				if erpnext.get_party_account_type(self.party_type) == "Receivable"
-				else "gl.credit_in_account_currency"
+				else "credit_in_account_currency"
 			)
 
 			if self.minimum_invoice_amount:
-				condition += " and `{0}` >= {1}".format(dr_or_cr, flt(self.minimum_payment_amount))
+				condition += " and gl.{dr_or_cr} >= {amount}".format(
+					dr_or_cr=dr_or_cr, amount=flt(self.minimum_payment_amount)
+				)
 			if self.maximum_invoice_amount:
-				condition += " and `{0}` <= {1}".format(dr_or_cr, flt(self.maximum_payment_amount))
+				condition += " and gl.{dr_or_cr} <= {amount}".format(
+					dr_or_cr=dr_or_cr, amount=flt(self.maximum_payment_amount)
+				)
 
 		else:
 			condition += (
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index e6a46d0..a5f9e24 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -811,7 +811,9 @@
 
 					if provisional_accounting_for_non_stock_items:
 						if item.purchase_receipt:
-							provisional_account = self.get_company_default("default_provisional_account")
+							provisional_account = frappe.db.get_value(
+								"Purchase Receipt Item", item.pr_detail, "provisional_expense_account"
+							) or self.get_company_default("default_provisional_account")
 							purchase_receipt_doc = purchase_receipt_doc_map.get(item.purchase_receipt)
 
 							if not purchase_receipt_doc:
@@ -834,7 +836,7 @@
 							if expense_booked_in_pr:
 								# Intentionally passing purchase invoice item to handle partial billing
 								purchase_receipt_doc.add_provisional_gl_entry(
-									item, gl_entries, self.posting_date, reverse=1
+									item, gl_entries, self.posting_date, provisional_account, reverse=1
 								)
 
 					if not self.is_internal_transfer():
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 73390dd..59bd637 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -1482,7 +1482,8 @@
 		self.assertEqual(payment_entry.taxes[0].allocated_amount, 0)
 
 	def test_provisional_accounting_entry(self):
-		item = create_item("_Test Non Stock Item", is_stock_item=0)
+		create_item("_Test Non Stock Item", is_stock_item=0)
+
 		provisional_account = create_account(
 			account_name="Provision Account",
 			parent_account="Current Liabilities - _TC",
@@ -1505,6 +1506,8 @@
 		pi.save()
 		pi.submit()
 
+		self.assertEquals(pr.items[0].provisional_expense_account, "Provision Account - _TC")
+
 		# Check GLE for Purchase Invoice
 		expected_gle = [
 			["Cost of Goods Sold - _TC", 250, 0, add_days(pr.posting_date, -1)],
diff --git a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py
index dcdba09..064b806 100644
--- a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py
@@ -65,7 +65,6 @@
 		)
 		sq.submit()
 
-		frappe.form_dict = frappe.local("form_dict")
 		frappe.form_dict.name = rfq.name
 
 		self.assertEqual(check_supplier_has_docname_access(supplier_wt_appos[0].get("supplier")), True)
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index eda3686..233b476 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -22,6 +22,9 @@
 
 
 class BuyingController(StockController, Subcontracting):
+	def __setup__(self):
+		self.flags.ignore_permlevel_for_fields = ["buying_price_list", "price_list_currency"]
+
 	def get_feed(self):
 		if self.get("supplier_name"):
 			return _("From {0} | {1} {2}").format(self.supplier_name, self.currency, self.grand_total)
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 7877827..19fedb3 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -16,6 +16,9 @@
 
 
 class SellingController(StockController):
+	def __setup__(self):
+		self.flags.ignore_permlevel_for_fields = ["selling_price_list", "price_list_currency"]
+
 	def get_feed(self):
 		return _("To {0} | {1} {2}").format(self.customer_name, self.currency, self.grand_total)
 
diff --git a/erpnext/erpnext_integrations/exotel_integration.py b/erpnext/erpnext_integrations/exotel_integration.py
index 1f5df67..522de9e 100644
--- a/erpnext/erpnext_integrations/exotel_integration.py
+++ b/erpnext/erpnext_integrations/exotel_integration.py
@@ -37,11 +37,26 @@
 
 @frappe.whitelist(allow_guest=True)
 def handle_missed_call(**kwargs):
-	update_call_log(kwargs, "Missed")
+	status = ""
+	call_type = kwargs.get("CallType")
+	dial_call_status = kwargs.get("DialCallStatus")
+
+	if call_type == "incomplete" and dial_call_status == "no-answer":
+		status = "No Answer"
+	elif call_type == "client-hangup" and dial_call_status == "canceled":
+		status = "Canceled"
+	elif call_type == "incomplete" and dial_call_status == "failed":
+		status = "Failed"
+
+	update_call_log(kwargs, status)
 
 
 def update_call_log(call_payload, status="Ringing", call_log=None):
 	call_log = call_log or get_call_log(call_payload)
+
+	# for a new sid, call_log and get_call_log will be empty so create a new log
+	if not call_log:
+		call_log = create_call_log(call_payload)
 	if call_log:
 		call_log.status = status
 		call_log.to = call_payload.get("DialWhomNumber")
@@ -53,16 +68,9 @@
 
 
 def get_call_log(call_payload):
-	call_log = frappe.get_all(
-		"Call Log",
-		{
-			"id": call_payload.get("CallSid"),
-		},
-		limit=1,
-	)
-
-	if call_log:
-		return frappe.get_doc("Call Log", call_log[0].name)
+	call_log_id = call_payload.get("CallSid")
+	if frappe.db.exists("Call Log", call_log_id):
+		return frappe.get_doc("Call Log", call_log_id)
 
 
 def create_call_log(call_payload):
diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json
index d592a9c..8a12f3b 100644
--- a/erpnext/hr/doctype/employee/employee.json
+++ b/erpnext/hr/doctype/employee/employee.json
@@ -4,7 +4,7 @@
  "allow_import": 1,
  "allow_rename": 1,
  "autoname": "naming_series:",
- "creation": "2013-03-07 09:04:18",
+ "creation": "2022-02-21 11:54:09.632218",
  "doctype": "DocType",
  "document_type": "Setup",
  "editable_grid": 1,
@@ -813,11 +813,12 @@
  "idx": 24,
  "image_field": "image",
  "links": [],
- "modified": "2021-06-17 11:31:37.730760",
+ "modified": "2022-03-22 13:44:37.088519",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Employee",
  "name_case": "Title Case",
+ "naming_rule": "By \"Naming Series\" field",
  "owner": "Administrator",
  "permissions": [
   {
@@ -857,7 +858,9 @@
  ],
  "search_fields": "employee_name",
  "show_name_in_global_search": 1,
+ "show_title_field_in_link": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "title_field": "employee_name"
 }
\ No newline at end of file
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js
index 035290d..5252798 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js
+++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js
@@ -140,26 +140,6 @@
 		}
 	}
 
-	start_date(doc, cdt, cdn) {
-		this.set_no_of_visits(doc, cdt, cdn);
-	}
-
-	end_date(doc, cdt, cdn) {
-		this.set_no_of_visits(doc, cdt, cdn);
-	}
-
-	periodicity(doc, cdt, cdn) {
-		this.set_no_of_visits(doc, cdt, cdn);
-	}
-
-	set_no_of_visits(doc, cdt, cdn) {
-		var item = frappe.get_doc(cdt, cdn);
-		let me = this;
-		if (item.start_date && item.periodicity) {
-			me.frm.call('validate_end_date_visits');
-
-		}
-	}
 };
 
 extend_cscript(cur_frm.cscript, new erpnext.maintenance.MaintenanceSchedule({frm: cur_frm}));
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
index 9a23c07..04c080c 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
@@ -213,6 +213,26 @@
 				if chk:
 					throw(_("Maintenance Schedule {0} exists against {1}").format(chk[0][0], d.sales_order))
 
+	def validate_items_table_change(self):
+		doc_before_save = self.get_doc_before_save()
+		if not doc_before_save:
+			return
+		for prev_item, item in zip(doc_before_save.items, self.items):
+			fields = [
+				"item_code",
+				"start_date",
+				"end_date",
+				"periodicity",
+				"sales_person",
+				"no_of_visits",
+				"serial_no",
+			]
+			for field in fields:
+				b_doc = prev_item.as_dict()
+				doc = item.as_dict()
+				if cstr(b_doc[field]) != cstr(doc[field]):
+					return True
+
 	def validate_no_of_visits(self):
 		return len(self.schedules) != sum(d.no_of_visits for d in self.items)
 
@@ -221,7 +241,7 @@
 		self.validate_maintenance_detail()
 		self.validate_dates_with_periodicity()
 		self.validate_sales_order()
-		if not self.schedules or self.validate_no_of_visits():
+		if not self.schedules or self.validate_items_table_change() or self.validate_no_of_visits():
 			self.generate_schedule()
 
 	def on_update(self):
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
index a98cd10..2268e0f 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
@@ -123,6 +123,36 @@
 
 		frappe.db.rollback()
 
+	def test_schedule_with_serials(self):
+		# Checks whether serials are automatically updated when changing in items table.
+		# Also checks if other fields trigger generate schdeule if changed in items table.
+		item_code = "_Test Serial Item"
+		make_serial_item_with_serial(item_code)
+		ms = make_maintenance_schedule(item_code=item_code, serial_no="TEST001, TEST002")
+		ms.save()
+
+		# Before Save
+		self.assertEqual(ms.schedules[0].serial_no, "TEST001, TEST002")
+		self.assertEqual(ms.schedules[0].sales_person, "Sales Team")
+		self.assertEqual(len(ms.schedules), 4)
+		self.assertFalse(ms.validate_items_table_change())
+		# After Save
+		ms.items[0].serial_no = "TEST001"
+		ms.items[0].sales_person = "_Test Sales Person"
+		ms.items[0].no_of_visits = 2
+		self.assertTrue(ms.validate_items_table_change())
+		ms.save()
+		self.assertEqual(ms.schedules[0].serial_no, "TEST001")
+		self.assertEqual(ms.schedules[0].sales_person, "_Test Sales Person")
+		self.assertEqual(len(ms.schedules), 2)
+		# When user manually deleted a row from schedules table.
+		ms.schedules.pop()
+		self.assertEqual(len(ms.schedules), 1)
+		ms.save()
+		self.assertEqual(len(ms.schedules), 2)
+
+		frappe.db.rollback()
+
 
 def make_serial_item_with_serial(item_code):
 	serial_item_doc = create_item(item_code, is_stock_item=1)
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 60b32b8..9ca05b9 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -462,6 +462,7 @@
 			work_order_data = {
 				"wip_warehouse": default_warehouses.get("wip_warehouse"),
 				"fg_warehouse": default_warehouses.get("fg_warehouse"),
+				"company": self.get("company"),
 			}
 
 			self.prepare_data_for_sub_assembly_items(row, work_order_data)
@@ -499,6 +500,7 @@
 
 		for supplier, po_list in subcontracted_po.items():
 			po = frappe.new_doc("Purchase Order")
+			po.company = self.company
 			po.supplier = supplier
 			po.schedule_date = getdate(po_list[0].schedule_date) if po_list[0].schedule_date else nowdate()
 			po.is_subcontracted = 1
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 6e5ffed..63b6bb7 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -262,8 +262,6 @@
 erpnext.patches.v13_0.update_shipment_status
 erpnext.patches.v13_0.remove_attribute_field_from_item_variant_setting
 erpnext.patches.v12_0.add_ewaybill_validity_field
-erpnext.patches.v13_0.germany_make_custom_fields
-erpnext.patches.v13_0.germany_fill_debtor_creditor_number
 erpnext.patches.v13_0.set_pos_closing_as_failed
 erpnext.patches.v13_0.rename_stop_to_send_birthday_reminders
 execute:frappe.rename_doc("Workspace", "Loan Management", "Loans", force=True)
@@ -343,6 +341,7 @@
 erpnext.patches.v14_0.delete_hub_doctypes
 erpnext.patches.v14_0.delete_hospitality_doctypes # 20-01-2022
 erpnext.patches.v14_0.delete_agriculture_doctypes
+erpnext.patches.v14_0.delete_datev_doctypes
 erpnext.patches.v14_0.rearrange_company_fields
 erpnext.patches.v14_0.update_leave_notification_template
 erpnext.patches.v14_0.restore_einvoice_fields
@@ -364,4 +363,4 @@
 erpnext.patches.v13_0.set_return_against_in_pos_invoice_references
 erpnext.patches.v13_0.remove_unknown_links_to_prod_plan_items # 24-03-2022
 erpnext.patches.v13_0.update_expense_claim_status_for_paid_advances
-erpnext.patches.v13_0.create_gst_custom_fields_in_quotation
\ No newline at end of file
+erpnext.patches.v13_0.create_gst_custom_fields_in_quotation
diff --git a/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py b/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py
deleted file mode 100644
index fc3e68a..0000000
--- a/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright (c) 2019, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-import frappe
-
-
-def execute():
-	"""Move account number into the new custom field debtor_creditor_number.
-
-	German companies used to use a dedicated payable/receivable account for
-	every party to mimick party accounts in the external accounting software
-	"DATEV". This is no longer necessary. The reference ID for DATEV will be
-	stored in a new custom field "debtor_creditor_number".
-	"""
-	company_list = frappe.get_all("Company", filters={"country": "Germany"})
-
-	for company in company_list:
-		party_account_list = frappe.get_all(
-			"Party Account",
-			filters={"company": company.name},
-			fields=["name", "account", "debtor_creditor_number"],
-		)
-		for party_account in party_account_list:
-			if (not party_account.account) or party_account.debtor_creditor_number:
-				# account empty or debtor_creditor_number already filled
-				continue
-
-			account_number = frappe.db.get_value("Account", party_account.account, "account_number")
-			if not account_number:
-				continue
-
-			frappe.db.set_value(
-				"Party Account", party_account.name, "debtor_creditor_number", account_number
-			)
-			frappe.db.set_value("Party Account", party_account.name, "account", "")
diff --git a/erpnext/patches/v13_0/germany_make_custom_fields.py b/erpnext/patches/v13_0/germany_make_custom_fields.py
deleted file mode 100644
index cc35813..0000000
--- a/erpnext/patches/v13_0/germany_make_custom_fields.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2019, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-import frappe
-
-from erpnext.regional.germany.setup import make_custom_fields
-
-
-def execute():
-	"""Execute the make_custom_fields method for german companies.
-
-	It is usually run once at setup of a new company. Since it's new, run it
-	once for existing companies as well.
-	"""
-	company_list = frappe.get_all("Company", filters={"country": "Germany"})
-	if not company_list:
-		return
-
-	make_custom_fields()
diff --git a/erpnext/patches/v14_0/delete_datev_doctypes.py b/erpnext/patches/v14_0/delete_datev_doctypes.py
new file mode 100644
index 0000000..a5de91f
--- /dev/null
+++ b/erpnext/patches/v14_0/delete_datev_doctypes.py
@@ -0,0 +1,13 @@
+import frappe
+
+
+def execute():
+	install_apps = frappe.get_installed_apps()
+	if "erpnext_datev_uo" in install_apps or "erpnext_datev" in install_apps:
+		return
+
+	# doctypes
+	frappe.delete_doc("DocType", "DATEV Settings", ignore_missing=True, force=True)
+
+	# reports
+	frappe.delete_doc("Report", "DATEV", ignore_missing=True, force=True)
diff --git a/erpnext/public/js/call_popup/call_popup.js b/erpnext/public/js/call_popup/call_popup.js
index c954f12..2dbe999 100644
--- a/erpnext/public/js/call_popup/call_popup.js
+++ b/erpnext/public/js/call_popup/call_popup.js
@@ -141,6 +141,14 @@
 				'fieldtype': 'Section Break',
 				'hide_border': 1,
 			}, {
+				'fieldname': 'call_type',
+				'label': 'Call Type',
+				'fieldtype': 'Link',
+				'options': 'Telephony Call Type',
+			}, {
+				'fieldtype': 'Section Break',
+				'hide_border': 1,
+			}, {
 				'fieldtype': 'Small Text',
 				'label': __('Call Summary'),
 				'fieldname': 'call_summary',
@@ -149,10 +157,12 @@
 				'label': __('Save'),
 				'click': () => {
 					const call_summary = this.call_details.get_value('call_summary');
+					const call_type = this.call_details.get_value('call_type');
 					if (!call_summary) return;
-					frappe.xcall('erpnext.telephony.doctype.call_log.call_log.add_call_summary', {
+					frappe.xcall('erpnext.telephony.doctype.call_log.call_log.add_call_summary_and_call_type', {
 						'call_log': this.call_log.name,
 						'summary': call_summary,
+						'call_type': call_type,
 					}).then(() => {
 						this.close_modal();
 						frappe.show_alert({
diff --git a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
index daf7a69..04e8211 100644
--- a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
+++ b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
@@ -19,7 +19,7 @@
 				)
 			).insert()
 
-			frappe.form_dict = dict(
+			frappe.local.form_dict = frappe._dict(
 				doctype="Quality Procedure",
 				quality_procedure_name="Test Child 1",
 				parent_quality_procedure=procedure.name,
diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.js b/erpnext/regional/doctype/datev_settings/datev_settings.js
deleted file mode 100644
index 3c36549..0000000
--- a/erpnext/regional/doctype/datev_settings/datev_settings.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('DATEV Settings', {
-	refresh: function(frm) {
-		frm.add_custom_button(__('Show Report'), () => frappe.set_route('query-report', 'DATEV'), "fa fa-table");
-	}
-});
diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.json b/erpnext/regional/doctype/datev_settings/datev_settings.json
deleted file mode 100644
index f60de4c..0000000
--- a/erpnext/regional/doctype/datev_settings/datev_settings.json
+++ /dev/null
@@ -1,125 +0,0 @@
-{
- "actions": [],
- "autoname": "field:client",
- "creation": "2019-08-13 23:56:34.259906",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "client",
-  "client_number",
-  "column_break_2",
-  "consultant_number",
-  "consultant",
-  "section_break_4",
-  "account_number_length",
-  "column_break_6",
-  "temporary_against_account_number"
- ],
- "fields": [
-  {
-   "fieldname": "client",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Client",
-   "options": "Company",
-   "reqd": 1,
-   "unique": 1
-  },
-  {
-   "fieldname": "client_number",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Client ID",
-   "length": 5,
-   "reqd": 1
-  },
-  {
-   "fieldname": "consultant",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Consultant",
-   "options": "Supplier"
-  },
-  {
-   "fieldname": "consultant_number",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Consultant ID",
-   "length": 7,
-   "reqd": 1
-  },
-  {
-   "fieldname": "column_break_2",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "section_break_4",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fieldname": "column_break_6",
-   "fieldtype": "Column Break"
-  },
-  {
-   "default": "4",
-   "fieldname": "account_number_length",
-   "fieldtype": "Int",
-   "label": "Account Number Length",
-   "reqd": 1
-  },
-  {
-   "allow_in_quick_entry": 1,
-   "fieldname": "temporary_against_account_number",
-   "fieldtype": "Data",
-   "label": "Temporary Against Account Number",
-   "reqd": 1
-  }
- ],
- "links": [],
- "modified": "2020-11-19 19:00:09.088816",
- "modified_by": "Administrator",
- "module": "Regional",
- "name": "DATEV 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
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Accounts Manager",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Accounts User",
-   "share": 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/datev_settings/datev_settings.py b/erpnext/regional/doctype/datev_settings/datev_settings.py
deleted file mode 100644
index 686a93e..0000000
--- a/erpnext/regional/doctype/datev_settings/datev_settings.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-# import frappe
-from frappe.model.document import Document
-
-
-class DATEVSettings(Document):
-	pass
diff --git a/erpnext/regional/doctype/datev_settings/test_datev_settings.py b/erpnext/regional/doctype/datev_settings/test_datev_settings.py
deleted file mode 100644
index ba70eb4..0000000
--- a/erpnext/regional/doctype/datev_settings/test_datev_settings.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-# import frappe
-import unittest
-
-
-class TestDATEVSettings(unittest.TestCase):
-	pass
diff --git a/erpnext/regional/germany/__init__.py b/erpnext/regional/germany/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/germany/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/germany/setup.py b/erpnext/regional/germany/setup.py
deleted file mode 100644
index b8e66c3..0000000
--- a/erpnext/regional/germany/setup.py
+++ /dev/null
@@ -1,35 +0,0 @@
-import frappe
-from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
-
-
-def setup(company=None, patch=True):
-	make_custom_fields()
-	add_custom_roles_for_reports()
-
-
-def make_custom_fields():
-	custom_fields = {
-		"Party Account": [
-			dict(
-				fieldname="debtor_creditor_number",
-				label="Debtor/Creditor Number",
-				fieldtype="Data",
-				insert_after="account",
-				translatable=0,
-			)
-		]
-	}
-
-	create_custom_fields(custom_fields)
-
-
-def add_custom_roles_for_reports():
-	"""Add Access Control to UAE VAT 201."""
-	if not frappe.db.get_value("Custom Role", dict(report="DATEV")):
-		frappe.get_doc(
-			dict(
-				doctype="Custom Role",
-				report="DATEV",
-				roles=[dict(role="Accounts User"), dict(role="Accounts Manager")],
-			)
-		).insert()
diff --git a/erpnext/regional/germany/utils/__init__.py b/erpnext/regional/germany/utils/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/germany/utils/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/germany/utils/datev/__init__.py b/erpnext/regional/germany/utils/datev/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/germany/utils/datev/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/germany/utils/datev/datev_constants.py b/erpnext/regional/germany/utils/datev/datev_constants.py
deleted file mode 100644
index 9524481..0000000
--- a/erpnext/regional/germany/utils/datev/datev_constants.py
+++ /dev/null
@@ -1,501 +0,0 @@
-"""Constants used in datev.py."""
-
-TRANSACTION_COLUMNS = [
-	# All possible columns must tbe listed here, because DATEV requires them to
-	# be present in the CSV.
-	# ---
-	# Umsatz
-	"Umsatz (ohne Soll/Haben-Kz)",
-	"Soll/Haben-Kennzeichen",
-	"WKZ Umsatz",
-	"Kurs",
-	"Basis-Umsatz",
-	"WKZ Basis-Umsatz",
-	# Konto/Gegenkonto
-	"Konto",
-	"Gegenkonto (ohne BU-Schlüssel)",
-	"BU-Schlüssel",
-	# Datum
-	"Belegdatum",
-	# Rechnungs- / Belegnummer
-	"Belegfeld 1",
-	# z.B. Fälligkeitsdatum Format: TTMMJJ
-	"Belegfeld 2",
-	# Skonto-Betrag / -Abzug (Der Wert 0 ist unzulässig)
-	"Skonto",
-	# Beschreibung des Buchungssatzes
-	"Buchungstext",
-	# Mahn- / Zahl-Sperre (1 = Postensperre)
-	"Postensperre",
-	"Diverse Adressnummer",
-	"Geschäftspartnerbank",
-	"Sachverhalt",
-	# Keine Mahnzinsen
-	"Zinssperre",
-	# Link auf den Buchungsbeleg (Programmkürzel + GUID)
-	"Beleglink",
-	# Beleginfo
-	"Beleginfo - Art 1",
-	"Beleginfo - Inhalt 1",
-	"Beleginfo - Art 2",
-	"Beleginfo - Inhalt 2",
-	"Beleginfo - Art 3",
-	"Beleginfo - Inhalt 3",
-	"Beleginfo - Art 4",
-	"Beleginfo - Inhalt 4",
-	"Beleginfo - Art 5",
-	"Beleginfo - Inhalt 5",
-	"Beleginfo - Art 6",
-	"Beleginfo - Inhalt 6",
-	"Beleginfo - Art 7",
-	"Beleginfo - Inhalt 7",
-	"Beleginfo - Art 8",
-	"Beleginfo - Inhalt 8",
-	# Zuordnung des Geschäftsvorfalls für die Kostenrechnung
-	"KOST1 - Kostenstelle",
-	"KOST2 - Kostenstelle",
-	"KOST-Menge",
-	# USt-ID-Nummer (Beispiel: DE133546770)
-	"EU-Mitgliedstaat u. USt-IdNr.",
-	# Der im EU-Bestimmungsland gültige Steuersatz
-	"EU-Steuersatz",
-	# I = Ist-Versteuerung,
-	# K = keine Umsatzsteuerrechnung
-	# P = Pauschalierung (z. B. für Land- und Forstwirtschaft),
-	# S = Soll-Versteuerung
-	"Abw. Versteuerungsart",
-	# Sachverhalte gem. § 13b Abs. 1 Satz 1 Nrn. 1.-5. UStG
-	"Sachverhalt L+L",
-	# Steuersatz / Funktion zum L+L-Sachverhalt (Beispiel: Wert 190 für 19%)
-	"Funktionsergänzung L+L",
-	# Bei Verwendung des BU-Schlüssels 49 für „andere Steuersätze“ muss der
-	# steuerliche Sachverhalt mitgegeben werden
-	"BU 49 Hauptfunktionstyp",
-	"BU 49 Hauptfunktionsnummer",
-	"BU 49 Funktionsergänzung",
-	# Zusatzinformationen, besitzen den Charakter eines Notizzettels und können
-	# frei erfasst werden.
-	"Zusatzinformation - Art 1",
-	"Zusatzinformation - Inhalt 1",
-	"Zusatzinformation - Art 2",
-	"Zusatzinformation - Inhalt 2",
-	"Zusatzinformation - Art 3",
-	"Zusatzinformation - Inhalt 3",
-	"Zusatzinformation - Art 4",
-	"Zusatzinformation - Inhalt 4",
-	"Zusatzinformation - Art 5",
-	"Zusatzinformation - Inhalt 5",
-	"Zusatzinformation - Art 6",
-	"Zusatzinformation - Inhalt 6",
-	"Zusatzinformation - Art 7",
-	"Zusatzinformation - Inhalt 7",
-	"Zusatzinformation - Art 8",
-	"Zusatzinformation - Inhalt 8",
-	"Zusatzinformation - Art 9",
-	"Zusatzinformation - Inhalt 9",
-	"Zusatzinformation - Art 10",
-	"Zusatzinformation - Inhalt 10",
-	"Zusatzinformation - Art 11",
-	"Zusatzinformation - Inhalt 11",
-	"Zusatzinformation - Art 12",
-	"Zusatzinformation - Inhalt 12",
-	"Zusatzinformation - Art 13",
-	"Zusatzinformation - Inhalt 13",
-	"Zusatzinformation - Art 14",
-	"Zusatzinformation - Inhalt 14",
-	"Zusatzinformation - Art 15",
-	"Zusatzinformation - Inhalt 15",
-	"Zusatzinformation - Art 16",
-	"Zusatzinformation - Inhalt 16",
-	"Zusatzinformation - Art 17",
-	"Zusatzinformation - Inhalt 17",
-	"Zusatzinformation - Art 18",
-	"Zusatzinformation - Inhalt 18",
-	"Zusatzinformation - Art 19",
-	"Zusatzinformation - Inhalt 19",
-	"Zusatzinformation - Art 20",
-	"Zusatzinformation - Inhalt 20",
-	# Wirkt sich nur bei Sachverhalt mit SKR 14 Land- und Forstwirtschaft aus,
-	# für andere SKR werden die Felder beim Import / Export überlesen bzw.
-	# leer exportiert.
-	"Stück",
-	"Gewicht",
-	# 1 = Lastschrift
-	# 2 = Mahnung
-	# 3 = Zahlung
-	"Zahlweise",
-	"Forderungsart",
-	# JJJJ
-	"Veranlagungsjahr",
-	# TTMMJJJJ
-	"Zugeordnete Fälligkeit",
-	# 1 = Einkauf von Waren
-	# 2 = Erwerb von Roh-Hilfs- und Betriebsstoffen
-	"Skontotyp",
-	# Allgemeine Bezeichnung, des Auftrags / Projekts.
-	"Auftragsnummer",
-	# AA = Angeforderte Anzahlung / Abschlagsrechnung
-	# AG = Erhaltene Anzahlung (Geldeingang)
-	# AV = Erhaltene Anzahlung (Verbindlichkeit)
-	# SR = Schlussrechnung
-	# SU = Schlussrechnung (Umbuchung)
-	# SG = Schlussrechnung (Geldeingang)
-	# SO = Sonstige
-	"Buchungstyp",
-	"USt-Schlüssel (Anzahlungen)",
-	"EU-Mitgliedstaat (Anzahlungen)",
-	"Sachverhalt L+L (Anzahlungen)",
-	"EU-Steuersatz (Anzahlungen)",
-	"Erlöskonto (Anzahlungen)",
-	# Wird beim Import durch SV (Stapelverarbeitung) ersetzt.
-	"Herkunft-Kz",
-	# Wird von DATEV verwendet.
-	"Leerfeld",
-	# Format TTMMJJJJ
-	"KOST-Datum",
-	# Vom Zahlungsempfänger individuell vergebenes Kennzeichen eines Mandats
-	# (z.B. Rechnungs- oder Kundennummer).
-	"SEPA-Mandatsreferenz",
-	# 1 = Skontosperre
-	# 0 = Keine Skontosperre
-	"Skontosperre",
-	# Gesellschafter und Sonderbilanzsachverhalt
-	"Gesellschaftername",
-	# Amtliche Nummer aus der Feststellungserklärung
-	"Beteiligtennummer",
-	"Identifikationsnummer",
-	"Zeichnernummer",
-	# Format TTMMJJJJ
-	"Postensperre bis",
-	# Gesellschafter und Sonderbilanzsachverhalt
-	"Bezeichnung SoBil-Sachverhalt",
-	"Kennzeichen SoBil-Buchung",
-	# 0 = keine Festschreibung
-	# 1 = Festschreibung
-	"Festschreibung",
-	# Format TTMMJJJJ
-	"Leistungsdatum",
-	# Format TTMMJJJJ
-	"Datum Zuord. Steuerperiode",
-	# OPOS-Informationen, Format TTMMJJJJ
-	"Fälligkeit",
-	# G oder 1 = Generalumkehr
-	# 0 = keine Generalumkehr
-	"Generalumkehr (GU)",
-	# Steuersatz für Steuerschlüssel
-	"Steuersatz",
-	# Beispiel: DE für Deutschland
-	"Land",
-]
-
-DEBTOR_CREDITOR_COLUMNS = [
-	# All possible columns must tbe listed here, because DATEV requires them to
-	# be present in the CSV.
-	# Columns "Leerfeld" have been replaced with "Leerfeld #" to not confuse pandas
-	# ---
-	"Konto",
-	"Name (Adressatentyp Unternehmen)",
-	"Unternehmensgegenstand",
-	"Name (Adressatentyp natürl. Person)",
-	"Vorname (Adressatentyp natürl. Person)",
-	"Name (Adressatentyp keine Angabe)",
-	"Adressatentyp",
-	"Kurzbezeichnung",
-	"EU-Land",
-	"EU-USt-IdNr.",
-	"Anrede",
-	"Titel/Akad. Grad",
-	"Adelstitel",
-	"Namensvorsatz",
-	"Adressart",
-	"Straße",
-	"Postfach",
-	"Postleitzahl",
-	"Ort",
-	"Land",
-	"Versandzusatz",
-	"Adresszusatz",
-	"Abweichende Anrede",
-	"Abw. Zustellbezeichnung 1",
-	"Abw. Zustellbezeichnung 2",
-	"Kennz. Korrespondenzadresse",
-	"Adresse gültig von",
-	"Adresse gültig bis",
-	"Telefon",
-	"Bemerkung (Telefon)",
-	"Telefon Geschäftsleitung",
-	"Bemerkung (Telefon GL)",
-	"E-Mail",
-	"Bemerkung (E-Mail)",
-	"Internet",
-	"Bemerkung (Internet)",
-	"Fax",
-	"Bemerkung (Fax)",
-	"Sonstige",
-	"Bemerkung (Sonstige)",
-	"Bankleitzahl 1",
-	"Bankbezeichnung 1",
-	"Bankkonto-Nummer 1",
-	"Länderkennzeichen 1",
-	"IBAN 1",
-	"Leerfeld 1",
-	"SWIFT-Code 1",
-	"Abw. Kontoinhaber 1",
-	"Kennz. Haupt-Bankverb. 1",
-	"Bankverb. 1 Gültig von",
-	"Bankverb. 1 Gültig bis",
-	"Bankleitzahl 2",
-	"Bankbezeichnung 2",
-	"Bankkonto-Nummer 2",
-	"Länderkennzeichen 2",
-	"IBAN 2",
-	"Leerfeld 2",
-	"SWIFT-Code 2",
-	"Abw. Kontoinhaber 2",
-	"Kennz. Haupt-Bankverb. 2",
-	"Bankverb. 2 gültig von",
-	"Bankverb. 2 gültig bis",
-	"Bankleitzahl 3",
-	"Bankbezeichnung 3",
-	"Bankkonto-Nummer 3",
-	"Länderkennzeichen 3",
-	"IBAN 3",
-	"Leerfeld 3",
-	"SWIFT-Code 3",
-	"Abw. Kontoinhaber 3",
-	"Kennz. Haupt-Bankverb. 3",
-	"Bankverb. 3 gültig von",
-	"Bankverb. 3 gültig bis",
-	"Bankleitzahl 4",
-	"Bankbezeichnung 4",
-	"Bankkonto-Nummer 4",
-	"Länderkennzeichen 4",
-	"IBAN 4",
-	"Leerfeld 4",
-	"SWIFT-Code 4",
-	"Abw. Kontoinhaber 4",
-	"Kennz. Haupt-Bankverb. 4",
-	"Bankverb. 4 Gültig von",
-	"Bankverb. 4 Gültig bis",
-	"Bankleitzahl 5",
-	"Bankbezeichnung 5",
-	"Bankkonto-Nummer 5",
-	"Länderkennzeichen 5",
-	"IBAN 5",
-	"Leerfeld 5",
-	"SWIFT-Code 5",
-	"Abw. Kontoinhaber 5",
-	"Kennz. Haupt-Bankverb. 5",
-	"Bankverb. 5 gültig von",
-	"Bankverb. 5 gültig bis",
-	"Leerfeld 6",
-	"Briefanrede",
-	"Grußformel",
-	"Kundennummer",
-	"Steuernummer",
-	"Sprache",
-	"Ansprechpartner",
-	"Vertreter",
-	"Sachbearbeiter",
-	"Diverse-Konto",
-	"Ausgabeziel",
-	"Währungssteuerung",
-	"Kreditlimit (Debitor)",
-	"Zahlungsbedingung",
-	"Fälligkeit in Tagen (Debitor)",
-	"Skonto in Prozent (Debitor)",
-	"Kreditoren-Ziel 1 (Tage)",
-	"Kreditoren-Skonto 1 (%)",
-	"Kreditoren-Ziel 2 (Tage)",
-	"Kreditoren-Skonto 2 (%)",
-	"Kreditoren-Ziel 3 Brutto (Tage)",
-	"Kreditoren-Ziel 4 (Tage)",
-	"Kreditoren-Skonto 4 (%)",
-	"Kreditoren-Ziel 5 (Tage)",
-	"Kreditoren-Skonto 5 (%)",
-	"Mahnung",
-	"Kontoauszug",
-	"Mahntext 1",
-	"Mahntext 2",
-	"Mahntext 3",
-	"Kontoauszugstext",
-	"Mahnlimit Betrag",
-	"Mahnlimit %",
-	"Zinsberechnung",
-	"Mahnzinssatz 1",
-	"Mahnzinssatz 2",
-	"Mahnzinssatz 3",
-	"Lastschrift",
-	"Verfahren",
-	"Mandantenbank",
-	"Zahlungsträger",
-	"Indiv. Feld 1",
-	"Indiv. Feld 2",
-	"Indiv. Feld 3",
-	"Indiv. Feld 4",
-	"Indiv. Feld 5",
-	"Indiv. Feld 6",
-	"Indiv. Feld 7",
-	"Indiv. Feld 8",
-	"Indiv. Feld 9",
-	"Indiv. Feld 10",
-	"Indiv. Feld 11",
-	"Indiv. Feld 12",
-	"Indiv. Feld 13",
-	"Indiv. Feld 14",
-	"Indiv. Feld 15",
-	"Abweichende Anrede (Rechnungsadresse)",
-	"Adressart (Rechnungsadresse)",
-	"Straße (Rechnungsadresse)",
-	"Postfach (Rechnungsadresse)",
-	"Postleitzahl (Rechnungsadresse)",
-	"Ort (Rechnungsadresse)",
-	"Land (Rechnungsadresse)",
-	"Versandzusatz (Rechnungsadresse)",
-	"Adresszusatz (Rechnungsadresse)",
-	"Abw. Zustellbezeichnung 1 (Rechnungsadresse)",
-	"Abw. Zustellbezeichnung 2 (Rechnungsadresse)",
-	"Adresse Gültig von (Rechnungsadresse)",
-	"Adresse Gültig bis (Rechnungsadresse)",
-	"Bankleitzahl 6",
-	"Bankbezeichnung 6",
-	"Bankkonto-Nummer 6",
-	"Länderkennzeichen 6",
-	"IBAN 6",
-	"Leerfeld 7",
-	"SWIFT-Code 6",
-	"Abw. Kontoinhaber 6",
-	"Kennz. Haupt-Bankverb. 6",
-	"Bankverb 6 gültig von",
-	"Bankverb 6 gültig bis",
-	"Bankleitzahl 7",
-	"Bankbezeichnung 7",
-	"Bankkonto-Nummer 7",
-	"Länderkennzeichen 7",
-	"IBAN 7",
-	"Leerfeld 8",
-	"SWIFT-Code 7",
-	"Abw. Kontoinhaber 7",
-	"Kennz. Haupt-Bankverb. 7",
-	"Bankverb 7 gültig von",
-	"Bankverb 7 gültig bis",
-	"Bankleitzahl 8",
-	"Bankbezeichnung 8",
-	"Bankkonto-Nummer 8",
-	"Länderkennzeichen 8",
-	"IBAN 8",
-	"Leerfeld 9",
-	"SWIFT-Code 8",
-	"Abw. Kontoinhaber 8",
-	"Kennz. Haupt-Bankverb. 8",
-	"Bankverb 8 gültig von",
-	"Bankverb 8 gültig bis",
-	"Bankleitzahl 9",
-	"Bankbezeichnung 9",
-	"Bankkonto-Nummer 9",
-	"Länderkennzeichen 9",
-	"IBAN 9",
-	"Leerfeld 10",
-	"SWIFT-Code 9",
-	"Abw. Kontoinhaber 9",
-	"Kennz. Haupt-Bankverb. 9",
-	"Bankverb 9 gültig von",
-	"Bankverb 9 gültig bis",
-	"Bankleitzahl 10",
-	"Bankbezeichnung 10",
-	"Bankkonto-Nummer 10",
-	"Länderkennzeichen 10",
-	"IBAN 10",
-	"Leerfeld 11",
-	"SWIFT-Code 10",
-	"Abw. Kontoinhaber 10",
-	"Kennz. Haupt-Bankverb. 10",
-	"Bankverb 10 gültig von",
-	"Bankverb 10 gültig bis",
-	"Nummer Fremdsystem",
-	"Insolvent",
-	"SEPA-Mandatsreferenz 1",
-	"SEPA-Mandatsreferenz 2",
-	"SEPA-Mandatsreferenz 3",
-	"SEPA-Mandatsreferenz 4",
-	"SEPA-Mandatsreferenz 5",
-	"SEPA-Mandatsreferenz 6",
-	"SEPA-Mandatsreferenz 7",
-	"SEPA-Mandatsreferenz 8",
-	"SEPA-Mandatsreferenz 9",
-	"SEPA-Mandatsreferenz 10",
-	"Verknüpftes OPOS-Konto",
-	"Mahnsperre bis",
-	"Lastschriftsperre bis",
-	"Zahlungssperre bis",
-	"Gebührenberechnung",
-	"Mahngebühr 1",
-	"Mahngebühr 2",
-	"Mahngebühr 3",
-	"Pauschalberechnung",
-	"Verzugspauschale 1",
-	"Verzugspauschale 2",
-	"Verzugspauschale 3",
-	"Alternativer Suchname",
-	"Status",
-	"Anschrift manuell geändert (Korrespondenzadresse)",
-	"Anschrift individuell (Korrespondenzadresse)",
-	"Anschrift manuell geändert (Rechnungsadresse)",
-	"Anschrift individuell (Rechnungsadresse)",
-	"Fristberechnung bei Debitor",
-	"Mahnfrist 1",
-	"Mahnfrist 2",
-	"Mahnfrist 3",
-	"Letzte Frist",
-]
-
-ACCOUNT_NAME_COLUMNS = [
-	# Account number
-	"Konto",
-	# Account name
-	"Kontenbeschriftung",
-	# Language of the account name
-	# "de-DE" or "en-GB"
-	"Sprach-ID",
-]
-
-
-class DataCategory:
-
-	"""Field of the CSV Header."""
-
-	DEBTORS_CREDITORS = "16"
-	ACCOUNT_NAMES = "20"
-	TRANSACTIONS = "21"
-	POSTING_TEXT_CONSTANTS = "67"
-
-
-class FormatName:
-
-	"""Field of the CSV Header, corresponds to DataCategory."""
-
-	DEBTORS_CREDITORS = "Debitoren/Kreditoren"
-	ACCOUNT_NAMES = "Kontenbeschriftungen"
-	TRANSACTIONS = "Buchungsstapel"
-	POSTING_TEXT_CONSTANTS = "Buchungstextkonstanten"
-
-
-class Transactions:
-	DATA_CATEGORY = DataCategory.TRANSACTIONS
-	FORMAT_NAME = FormatName.TRANSACTIONS
-	FORMAT_VERSION = "9"
-	COLUMNS = TRANSACTION_COLUMNS
-
-
-class DebtorsCreditors:
-	DATA_CATEGORY = DataCategory.DEBTORS_CREDITORS
-	FORMAT_NAME = FormatName.DEBTORS_CREDITORS
-	FORMAT_VERSION = "5"
-	COLUMNS = DEBTOR_CREDITOR_COLUMNS
-
-
-class AccountNames:
-	DATA_CATEGORY = DataCategory.ACCOUNT_NAMES
-	FORMAT_NAME = FormatName.ACCOUNT_NAMES
-	FORMAT_VERSION = "2"
-	COLUMNS = ACCOUNT_NAME_COLUMNS
diff --git a/erpnext/regional/germany/utils/datev/datev_csv.py b/erpnext/regional/germany/utils/datev/datev_csv.py
deleted file mode 100644
index d4e9c27..0000000
--- a/erpnext/regional/germany/utils/datev/datev_csv.py
+++ /dev/null
@@ -1,184 +0,0 @@
-import datetime
-import zipfile
-from csv import QUOTE_NONNUMERIC
-from io import BytesIO
-
-import frappe
-import pandas as pd
-from frappe import _
-
-from .datev_constants import DataCategory
-
-
-def get_datev_csv(data, filters, csv_class):
-	"""
-	Fill in missing columns and return a CSV in DATEV Format.
-
-	For automatic processing, DATEV requires the first line of the CSV file to
-	hold meta data such as the length of account numbers oder the category of
-	the data.
-
-	Arguments:
-	data -- array of dictionaries
-	filters -- dict
-	csv_class -- defines DATA_CATEGORY, FORMAT_NAME and COLUMNS
-	"""
-	empty_df = pd.DataFrame(columns=csv_class.COLUMNS)
-	data_df = pd.DataFrame.from_records(data)
-	result = empty_df.append(data_df, sort=True)
-
-	if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS:
-		result["Belegdatum"] = pd.to_datetime(result["Belegdatum"])
-
-		result["Beleginfo - Inhalt 6"] = pd.to_datetime(result["Beleginfo - Inhalt 6"])
-		result["Beleginfo - Inhalt 6"] = result["Beleginfo - Inhalt 6"].dt.strftime("%d%m%Y")
-
-		result["Fälligkeit"] = pd.to_datetime(result["Fälligkeit"])
-		result["Fälligkeit"] = result["Fälligkeit"].dt.strftime("%d%m%y")
-
-		result.sort_values(by="Belegdatum", inplace=True, kind="stable", ignore_index=True)
-
-	if csv_class.DATA_CATEGORY == DataCategory.ACCOUNT_NAMES:
-		result["Sprach-ID"] = "de-DE"
-
-	data = result.to_csv(
-		# Reason for str(';'): https://github.com/pandas-dev/pandas/issues/6035
-		sep=";",
-		# European decimal seperator
-		decimal=",",
-		# Windows "ANSI" encoding
-		encoding="latin_1",
-		# format date as DDMM
-		date_format="%d%m",
-		# Windows line terminator
-		line_terminator="\r\n",
-		# Do not number rows
-		index=False,
-		# Use all columns defined above
-		columns=csv_class.COLUMNS,
-		# Quote most fields, even currency values with "," separator
-		quoting=QUOTE_NONNUMERIC,
-	)
-
-	data = data.encode("latin_1", errors="replace")
-
-	header = get_header(filters, csv_class)
-	header = ";".join(header).encode("latin_1", errors="replace")
-
-	# 1st Row: Header with meta data
-	# 2nd Row: Data heading (Überschrift der Nutzdaten), included in `data` here.
-	# 3rd - nth Row: Data (Nutzdaten)
-	return header + b"\r\n" + data
-
-
-def get_header(filters, csv_class):
-	description = filters.get("voucher_type", csv_class.FORMAT_NAME)
-	company = filters.get("company")
-	datev_settings = frappe.get_doc("DATEV Settings", {"client": company})
-	default_currency = frappe.get_value("Company", company, "default_currency")
-	coa = frappe.get_value("Company", company, "chart_of_accounts")
-	coa_short_code = "04" if "SKR04" in coa else ("03" if "SKR03" in coa else "")
-
-	header = [
-		# DATEV format
-		# 	"DTVF" = created by DATEV software,
-		# 	"EXTF" = created by other software
-		'"EXTF"',
-		# version of the DATEV format
-		# 	141 = 1.41,
-		# 	510 = 5.10,
-		# 	720 = 7.20
-		"700",
-		csv_class.DATA_CATEGORY,
-		'"%s"' % csv_class.FORMAT_NAME,
-		# Format version (regarding format name)
-		csv_class.FORMAT_VERSION,
-		# Generated on
-		datetime.datetime.now().strftime("%Y%m%d%H%M%S") + "000",
-		# Imported on -- stays empty
-		"",
-		# Origin. Any two symbols, will be replaced by "SV" on import.
-		'"EN"',
-		# I = Exported by
-		'"%s"' % frappe.session.user,
-		# J = Imported by -- stays empty
-		"",
-		# K = Tax consultant number (Beraternummer)
-		datev_settings.get("consultant_number", "0000000"),
-		# L = Tax client number (Mandantennummer)
-		datev_settings.get("client_number", "00000"),
-		# M = Start of the fiscal year (Wirtschaftsjahresbeginn)
-		frappe.utils.formatdate(filters.get("fiscal_year_start"), "yyyyMMdd"),
-		# N = Length of account numbers (Sachkontenlänge)
-		str(filters.get("account_number_length", 4)),
-		# O = Transaction batch start date (YYYYMMDD)
-		frappe.utils.formatdate(filters.get("from_date"), "yyyyMMdd")
-		if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS
-		else "",
-		# P = Transaction batch end date (YYYYMMDD)
-		frappe.utils.formatdate(filters.get("to_date"), "yyyyMMdd")
-		if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS
-		else "",
-		# Q = Description (for example, "Sales Invoice") Max. 30 chars
-		'"{}"'.format(_(description)) if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else "",
-		# R = Diktatkürzel
-		"",
-		# S = Buchungstyp
-		# 	1 = Transaction batch (Finanzbuchführung),
-		# 	2 = Annual financial statement (Jahresabschluss)
-		"1" if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else "",
-		# T = Rechnungslegungszweck
-		# 	0 oder leer = vom Rechnungslegungszweck unabhängig
-		# 	50 = Handelsrecht
-		# 	30 = Steuerrecht
-		# 	64 = IFRS
-		# 	40 = Kalkulatorik
-		# 	11 = Reserviert
-		# 	12 = Reserviert
-		"0" if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else "",
-		# U = Festschreibung
-		# TODO: Filter by Accounting Period. In export for closed Accounting Period, this will be "1"
-		"0",
-		# V = Default currency, for example, "EUR"
-		'"%s"' % default_currency if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else "",
-		# reserviert
-		"",
-		# Derivatskennzeichen
-		"",
-		# reserviert
-		"",
-		# reserviert
-		"",
-		# SKR
-		'"%s"' % coa_short_code,
-		# Branchen-Lösungs-ID
-		"",
-		# reserviert
-		"",
-		# reserviert
-		"",
-		# Anwendungsinformation (Verarbeitungskennzeichen der abgebenden Anwendung)
-		"",
-	]
-	return header
-
-
-def zip_and_download(zip_filename, csv_files):
-	"""
-	Put CSV files in a zip archive and send that to the client.
-
-	Params:
-	zip_filename	Name of the zip file
-	csv_files		list of dicts [{'file_name': 'my_file.csv', 'csv_data': 'comma,separated,values'}]
-	"""
-	zip_buffer = BytesIO()
-
-	zip_file = zipfile.ZipFile(zip_buffer, mode="w", compression=zipfile.ZIP_DEFLATED)
-	for csv_file in csv_files:
-		zip_file.writestr(csv_file.get("file_name"), csv_file.get("csv_data"))
-
-	zip_file.close()
-
-	frappe.response["filecontent"] = zip_buffer.getvalue()
-	frappe.response["filename"] = zip_filename
-	frappe.response["type"] = "binary"
diff --git a/erpnext/regional/report/datev/__init__.py b/erpnext/regional/report/datev/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/report/datev/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/report/datev/datev.js b/erpnext/regional/report/datev/datev.js
deleted file mode 100644
index 03c729e..0000000
--- a/erpnext/regional/report/datev/datev.js
+++ /dev/null
@@ -1,56 +0,0 @@
-frappe.query_reports["DATEV"] = {
-	"filters": [
-		{
-			"fieldname": "company",
-			"label": __("Company"),
-			"fieldtype": "Link",
-			"options": "Company",
-			"default": frappe.defaults.get_user_default("Company") || frappe.defaults.get_global_default("Company"),
-			"reqd": 1
-		},
-		{
-			"fieldname": "from_date",
-			"label": __("From Date"),
-			"default": moment().subtract(1, 'month').startOf('month').format(),
-			"fieldtype": "Date",
-			"reqd": 1
-		},
-		{
-			"fieldname": "to_date",
-			"label": __("To Date"),
-			"default": moment().subtract(1, 'month').endOf('month').format(),
-			"fieldtype": "Date",
-			"reqd": 1
-		},
-		{
-			"fieldname": "voucher_type",
-			"label": __("Voucher Type"),
-			"fieldtype": "Select",
-			"options": "\nSales Invoice\nPurchase Invoice\nPayment Entry\nExpense Claim\nPayroll Entry\nBank Reconciliation\nAsset\nStock Entry"
-		}
-	],
-	onload: function(query_report) {
-		let company = frappe.query_report.get_filter_value('company');
-		frappe.db.exists('DATEV Settings', company).then((settings_exist) => {
-			if (!settings_exist) {
-				frappe.confirm(__('DATEV Settings for your Company are missing. Would you like to create them now?'),
-					() => frappe.new_doc('DATEV Settings', {'company': company})
-				);
-			}
-		});
-
-		query_report.page.add_menu_item(__("Download DATEV File"), () => {
-			const filters = encodeURIComponent(
-				JSON.stringify(
-					query_report.get_values()
-				)
-			);
-			window.open(`/api/method/erpnext.regional.report.datev.datev.download_datev_csv?filters=${filters}`);
-		});
-
-		query_report.page.add_menu_item(__("Change DATEV Settings"), () => {
-			let company = frappe.query_report.get_filter_value('company'); // read company from filters again – it might have changed by now.
-			frappe.set_route('Form', 'DATEV Settings', company);
-		});
-	}
-};
diff --git a/erpnext/regional/report/datev/datev.json b/erpnext/regional/report/datev/datev.json
deleted file mode 100644
index 94e3960..0000000
--- a/erpnext/regional/report/datev/datev.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "add_total_row": 0,
- "columns": [],
- "creation": "2019-04-24 08:45:16.650129",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "filters": [],
- "idx": 0,
- "is_standard": "Yes",
- "modified": "2021-04-06 12:23:00.379517",
- "modified_by": "Administrator",
- "module": "Regional",
- "name": "DATEV",
- "owner": "Administrator",
- "prepared_report": 0,
- "ref_doctype": "GL Entry",
- "report_name": "DATEV",
- "report_type": "Script Report",
- "roles": []
-}
\ No newline at end of file
diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py
deleted file mode 100644
index 2d888a8..0000000
--- a/erpnext/regional/report/datev/datev.py
+++ /dev/null
@@ -1,570 +0,0 @@
-"""
-Provide a report and downloadable CSV according to the German DATEV format.
-
-- Query report showing only the columns that contain data, formatted nicely for
-	dispay to the user.
-- CSV download functionality `download_datev_csv` that provides a CSV file with
-	all required columns. Used to import the data into the DATEV Software.
-"""
-
-import json
-
-import frappe
-from frappe import _
-
-from erpnext.accounts.utils import get_fiscal_year
-from erpnext.regional.germany.utils.datev.datev_constants import (
-	AccountNames,
-	DebtorsCreditors,
-	Transactions,
-)
-from erpnext.regional.germany.utils.datev.datev_csv import get_datev_csv, zip_and_download
-
-COLUMNS = [
-	{
-		"label": "Umsatz (ohne Soll/Haben-Kz)",
-		"fieldname": "Umsatz (ohne Soll/Haben-Kz)",
-		"fieldtype": "Currency",
-		"width": 100,
-	},
-	{
-		"label": "Soll/Haben-Kennzeichen",
-		"fieldname": "Soll/Haben-Kennzeichen",
-		"fieldtype": "Data",
-		"width": 100,
-	},
-	{"label": "Konto", "fieldname": "Konto", "fieldtype": "Data", "width": 100},
-	{
-		"label": "Gegenkonto (ohne BU-Schlüssel)",
-		"fieldname": "Gegenkonto (ohne BU-Schlüssel)",
-		"fieldtype": "Data",
-		"width": 100,
-	},
-	{"label": "BU-Schlüssel", "fieldname": "BU-Schlüssel", "fieldtype": "Data", "width": 100},
-	{"label": "Belegdatum", "fieldname": "Belegdatum", "fieldtype": "Date", "width": 100},
-	{"label": "Belegfeld 1", "fieldname": "Belegfeld 1", "fieldtype": "Data", "width": 150},
-	{"label": "Buchungstext", "fieldname": "Buchungstext", "fieldtype": "Text", "width": 300},
-	{
-		"label": "Beleginfo - Art 1",
-		"fieldname": "Beleginfo - Art 1",
-		"fieldtype": "Link",
-		"options": "DocType",
-		"width": 100,
-	},
-	{
-		"label": "Beleginfo - Inhalt 1",
-		"fieldname": "Beleginfo - Inhalt 1",
-		"fieldtype": "Dynamic Link",
-		"options": "Beleginfo - Art 1",
-		"width": 150,
-	},
-	{
-		"label": "Beleginfo - Art 2",
-		"fieldname": "Beleginfo - Art 2",
-		"fieldtype": "Link",
-		"options": "DocType",
-		"width": 100,
-	},
-	{
-		"label": "Beleginfo - Inhalt 2",
-		"fieldname": "Beleginfo - Inhalt 2",
-		"fieldtype": "Dynamic Link",
-		"options": "Beleginfo - Art 2",
-		"width": 150,
-	},
-	{
-		"label": "Beleginfo - Art 3",
-		"fieldname": "Beleginfo - Art 3",
-		"fieldtype": "Link",
-		"options": "DocType",
-		"width": 100,
-	},
-	{
-		"label": "Beleginfo - Inhalt 3",
-		"fieldname": "Beleginfo - Inhalt 3",
-		"fieldtype": "Dynamic Link",
-		"options": "Beleginfo - Art 3",
-		"width": 150,
-	},
-	{
-		"label": "Beleginfo - Art 4",
-		"fieldname": "Beleginfo - Art 4",
-		"fieldtype": "Data",
-		"width": 100,
-	},
-	{
-		"label": "Beleginfo - Inhalt 4",
-		"fieldname": "Beleginfo - Inhalt 4",
-		"fieldtype": "Data",
-		"width": 150,
-	},
-	{
-		"label": "Beleginfo - Art 5",
-		"fieldname": "Beleginfo - Art 5",
-		"fieldtype": "Data",
-		"width": 150,
-	},
-	{
-		"label": "Beleginfo - Inhalt 5",
-		"fieldname": "Beleginfo - Inhalt 5",
-		"fieldtype": "Data",
-		"width": 100,
-	},
-	{
-		"label": "Beleginfo - Art 6",
-		"fieldname": "Beleginfo - Art 6",
-		"fieldtype": "Data",
-		"width": 150,
-	},
-	{
-		"label": "Beleginfo - Inhalt 6",
-		"fieldname": "Beleginfo - Inhalt 6",
-		"fieldtype": "Date",
-		"width": 100,
-	},
-	{"label": "Fälligkeit", "fieldname": "Fälligkeit", "fieldtype": "Date", "width": 100},
-]
-
-
-def execute(filters=None):
-	"""Entry point for frappe."""
-	data = []
-	if filters and validate(filters):
-		fn = "temporary_against_account_number"
-		filters[fn] = frappe.get_value("DATEV Settings", filters.get("company"), fn)
-		data = get_transactions(filters, as_dict=0)
-
-	return COLUMNS, data
-
-
-def validate(filters):
-	"""Make sure all mandatory filters and settings are present."""
-	company = filters.get("company")
-	if not company:
-		frappe.throw(_("<b>Company</b> is a mandatory filter."))
-
-	from_date = filters.get("from_date")
-	if not from_date:
-		frappe.throw(_("<b>From Date</b> is a mandatory filter."))
-
-	to_date = filters.get("to_date")
-	if not to_date:
-		frappe.throw(_("<b>To Date</b> is a mandatory filter."))
-
-	validate_fiscal_year(from_date, to_date, company)
-
-	if not frappe.db.exists("DATEV Settings", filters.get("company")):
-		msg = "Please create DATEV Settings for Company {}".format(filters.get("company"))
-		frappe.log_error(msg, title="DATEV Settings missing")
-		return False
-
-	return True
-
-
-def validate_fiscal_year(from_date, to_date, company):
-	from_fiscal_year = get_fiscal_year(date=from_date, company=company)
-	to_fiscal_year = get_fiscal_year(date=to_date, company=company)
-	if from_fiscal_year != to_fiscal_year:
-		frappe.throw(_("Dates {} and {} are not in the same fiscal year.").format(from_date, to_date))
-
-
-def get_transactions(filters, as_dict=1):
-	def run(params_method, filters):
-		extra_fields, extra_joins, extra_filters = params_method(filters)
-		return run_query(filters, extra_fields, extra_joins, extra_filters, as_dict=as_dict)
-
-	def sort_by(row):
-		# "Belegdatum" is in the fifth column when list format is used
-		return row["Belegdatum" if as_dict else 5]
-
-	type_map = {
-		# specific query methods for some voucher types
-		"Payment Entry": get_payment_entry_params,
-		"Sales Invoice": get_sales_invoice_params,
-		"Purchase Invoice": get_purchase_invoice_params,
-	}
-
-	only_voucher_type = filters.get("voucher_type")
-	transactions = []
-
-	for voucher_type, get_voucher_params in type_map.items():
-		if only_voucher_type and only_voucher_type != voucher_type:
-			continue
-
-		transactions.extend(run(params_method=get_voucher_params, filters=filters))
-
-	if not only_voucher_type or only_voucher_type not in type_map:
-		# generic query method for all other voucher types
-		filters["exclude_voucher_types"] = type_map.keys()
-		transactions.extend(run(params_method=get_generic_params, filters=filters))
-
-	return sorted(transactions, key=sort_by)
-
-
-def get_payment_entry_params(filters):
-	extra_fields = """
-		, 'Zahlungsreferenz' as 'Beleginfo - Art 5'
-		, pe.reference_no as 'Beleginfo - Inhalt 5'
-		, 'Buchungstag' as 'Beleginfo - Art 6'
-		, pe.reference_date as 'Beleginfo - Inhalt 6'
-		, '' as 'Fälligkeit'
-	"""
-
-	extra_joins = """
-		LEFT JOIN `tabPayment Entry` pe
-		ON gl.voucher_no = pe.name
-	"""
-
-	extra_filters = """
-		AND gl.voucher_type = 'Payment Entry'
-	"""
-
-	return extra_fields, extra_joins, extra_filters
-
-
-def get_sales_invoice_params(filters):
-	extra_fields = """
-		, '' as 'Beleginfo - Art 5'
-		, '' as 'Beleginfo - Inhalt 5'
-		, '' as 'Beleginfo - Art 6'
-		, '' as 'Beleginfo - Inhalt 6'
-		, si.due_date as 'Fälligkeit'
-	"""
-
-	extra_joins = """
-		LEFT JOIN `tabSales Invoice` si
-		ON gl.voucher_no = si.name
-	"""
-
-	extra_filters = """
-		AND gl.voucher_type = 'Sales Invoice'
-	"""
-
-	return extra_fields, extra_joins, extra_filters
-
-
-def get_purchase_invoice_params(filters):
-	extra_fields = """
-		, 'Lieferanten-Rechnungsnummer' as 'Beleginfo - Art 5'
-		, pi.bill_no as 'Beleginfo - Inhalt 5'
-		, 'Lieferanten-Rechnungsdatum' as 'Beleginfo - Art 6'
-		, pi.bill_date as 'Beleginfo - Inhalt 6'
-		, pi.due_date as 'Fälligkeit'
-	"""
-
-	extra_joins = """
-		LEFT JOIN `tabPurchase Invoice` pi
-		ON gl.voucher_no = pi.name
-	"""
-
-	extra_filters = """
-		AND gl.voucher_type = 'Purchase Invoice'
-	"""
-
-	return extra_fields, extra_joins, extra_filters
-
-
-def get_generic_params(filters):
-	# produce empty fields so all rows will have the same length
-	extra_fields = """
-		, '' as 'Beleginfo - Art 5'
-		, '' as 'Beleginfo - Inhalt 5'
-		, '' as 'Beleginfo - Art 6'
-		, '' as 'Beleginfo - Inhalt 6'
-		, '' as 'Fälligkeit'
-	"""
-	extra_joins = ""
-
-	if filters.get("exclude_voucher_types"):
-		# exclude voucher types that are queried by a dedicated method
-		exclude = "({})".format(
-			", ".join("'{}'".format(key) for key in filters.get("exclude_voucher_types"))
-		)
-		extra_filters = "AND gl.voucher_type NOT IN {}".format(exclude)
-
-	# if voucher type filter is set, allow only this type
-	if filters.get("voucher_type"):
-		extra_filters += " AND gl.voucher_type = %(voucher_type)s"
-
-	return extra_fields, extra_joins, extra_filters
-
-
-def run_query(filters, extra_fields, extra_joins, extra_filters, as_dict=1):
-	"""
-	Get a list of accounting entries.
-
-	Select GL Entries joined with Account and Party Account in order to get the
-	account numbers. Returns a list of accounting entries.
-
-	Arguments:
-	filters -- dict of filters to be passed to the sql query
-	as_dict -- return as list of dicts [0,1]
-	"""
-	query = """
-		SELECT
-
-			/* either debit or credit amount; always positive */
-			case gl.debit when 0 then gl.credit else gl.debit end as 'Umsatz (ohne Soll/Haben-Kz)',
-
-			/* 'H' when credit, 'S' when debit */
-			case gl.debit when 0 then 'H' else 'S' end as 'Soll/Haben-Kennzeichen',
-
-			/* account number or, if empty, party account number */
-			acc.account_number as 'Konto',
-
-			/* against number or, if empty, party against number */
-			%(temporary_against_account_number)s as 'Gegenkonto (ohne BU-Schlüssel)',
-
-			'' as 'BU-Schlüssel',
-
-			gl.posting_date as 'Belegdatum',
-			gl.voucher_no as 'Belegfeld 1',
-			REPLACE(LEFT(gl.remarks, 60), '\n', ' ') as 'Buchungstext',
-			gl.voucher_type as 'Beleginfo - Art 1',
-			gl.voucher_no as 'Beleginfo - Inhalt 1',
-			gl.against_voucher_type as 'Beleginfo - Art 2',
-			gl.against_voucher as 'Beleginfo - Inhalt 2',
-			gl.party_type as 'Beleginfo - Art 3',
-			gl.party as 'Beleginfo - Inhalt 3',
-			case gl.party_type when 'Customer' then 'Debitorennummer' when 'Supplier' then 'Kreditorennummer' else NULL end as 'Beleginfo - Art 4',
-			par.debtor_creditor_number as 'Beleginfo - Inhalt 4'
-
-			{extra_fields}
-
-		FROM `tabGL Entry` gl
-
-			/* Kontonummer */
-			LEFT JOIN `tabAccount` acc
-			ON gl.account = acc.name
-
-			LEFT JOIN `tabParty Account` par
-			ON par.parent = gl.party
-			AND par.parenttype = gl.party_type
-			AND par.company = %(company)s
-
-			{extra_joins}
-
-		WHERE gl.company = %(company)s
-		AND DATE(gl.posting_date) >= %(from_date)s
-		AND DATE(gl.posting_date) <= %(to_date)s
-
-		{extra_filters}
-
-		ORDER BY 'Belegdatum', gl.voucher_no""".format(
-		extra_fields=extra_fields, extra_joins=extra_joins, extra_filters=extra_filters
-	)
-
-	gl_entries = frappe.db.sql(query, filters, as_dict=as_dict)
-
-	return gl_entries
-
-
-def get_customers(filters):
-	"""
-	Get a list of Customers.
-
-	Arguments:
-	filters -- dict of filters to be passed to the sql query
-	"""
-	return frappe.db.sql(
-		"""
-		SELECT
-
-			par.debtor_creditor_number as 'Konto',
-			CASE cus.customer_type
-				WHEN 'Company' THEN cus.customer_name
-				ELSE null
-				END as 'Name (Adressatentyp Unternehmen)',
-			CASE cus.customer_type
-				WHEN 'Individual' THEN TRIM(SUBSTR(cus.customer_name, LOCATE(' ', cus.customer_name)))
-				ELSE null
-				END as 'Name (Adressatentyp natürl. Person)',
-			CASE cus.customer_type
-				WHEN 'Individual' THEN SUBSTRING_INDEX(SUBSTRING_INDEX(cus.customer_name, ' ', 1), ' ', -1)
-				ELSE null
-				END as 'Vorname (Adressatentyp natürl. Person)',
-			CASE cus.customer_type
-				WHEN 'Individual' THEN '1'
-				WHEN 'Company' THEN '2'
-				ELSE '0'
-				END as 'Adressatentyp',
-			adr.address_line1 as 'Straße',
-			adr.pincode as 'Postleitzahl',
-			adr.city as 'Ort',
-			UPPER(country.code) as 'Land',
-			adr.address_line2 as 'Adresszusatz',
-			adr.email_id as 'E-Mail',
-			adr.phone as 'Telefon',
-			adr.fax as 'Fax',
-			cus.website as 'Internet',
-			cus.tax_id as 'Steuernummer'
-
-		FROM `tabCustomer` cus
-
-			left join `tabParty Account` par
-			on par.parent = cus.name
-			and par.parenttype = 'Customer'
-			and par.company = %(company)s
-
-			left join `tabDynamic Link` dyn_adr
-			on dyn_adr.link_name = cus.name
-			and dyn_adr.link_doctype = 'Customer'
-			and dyn_adr.parenttype = 'Address'
-
-			left join `tabAddress` adr
-			on adr.name = dyn_adr.parent
-			and adr.is_primary_address = '1'
-
-			left join `tabCountry` country
-			on country.name = adr.country
-
-		WHERE adr.is_primary_address = '1'
-		""",
-		filters,
-		as_dict=1,
-	)
-
-
-def get_suppliers(filters):
-	"""
-	Get a list of Suppliers.
-
-	Arguments:
-	filters -- dict of filters to be passed to the sql query
-	"""
-	return frappe.db.sql(
-		"""
-		SELECT
-
-			par.debtor_creditor_number as 'Konto',
-			CASE sup.supplier_type
-				WHEN 'Company' THEN sup.supplier_name
-				ELSE null
-				END as 'Name (Adressatentyp Unternehmen)',
-			CASE sup.supplier_type
-				WHEN 'Individual' THEN TRIM(SUBSTR(sup.supplier_name, LOCATE(' ', sup.supplier_name)))
-				ELSE null
-				END as 'Name (Adressatentyp natürl. Person)',
-			CASE sup.supplier_type
-				WHEN 'Individual' THEN SUBSTRING_INDEX(SUBSTRING_INDEX(sup.supplier_name, ' ', 1), ' ', -1)
-				ELSE null
-				END as 'Vorname (Adressatentyp natürl. Person)',
-			CASE sup.supplier_type
-				WHEN 'Individual' THEN '1'
-				WHEN 'Company' THEN '2'
-				ELSE '0'
-				END as 'Adressatentyp',
-			adr.address_line1 as 'Straße',
-			adr.pincode as 'Postleitzahl',
-			adr.city as 'Ort',
-			UPPER(country.code) as 'Land',
-			adr.address_line2 as 'Adresszusatz',
-			adr.email_id as 'E-Mail',
-			adr.phone as 'Telefon',
-			adr.fax as 'Fax',
-			sup.website as 'Internet',
-			sup.tax_id as 'Steuernummer',
-			case sup.on_hold when 1 then sup.release_date else null end as 'Zahlungssperre bis'
-
-		FROM `tabSupplier` sup
-
-			left join `tabParty Account` par
-			on par.parent = sup.name
-			and par.parenttype = 'Supplier'
-			and par.company = %(company)s
-
-			left join `tabDynamic Link` dyn_adr
-			on dyn_adr.link_name = sup.name
-			and dyn_adr.link_doctype = 'Supplier'
-			and dyn_adr.parenttype = 'Address'
-
-			left join `tabAddress` adr
-			on adr.name = dyn_adr.parent
-			and adr.is_primary_address = '1'
-
-			left join `tabCountry` country
-			on country.name = adr.country
-
-		WHERE adr.is_primary_address = '1'
-		""",
-		filters,
-		as_dict=1,
-	)
-
-
-def get_account_names(filters):
-	return frappe.db.sql(
-		"""
-		SELECT
-
-			account_number as 'Konto',
-			LEFT(account_name, 40) as 'Kontenbeschriftung',
-			'de-DE' as 'Sprach-ID'
-
-		FROM `tabAccount`
-		WHERE company = %(company)s
-		AND is_group = 0
-		AND account_number != ''
-	""",
-		filters,
-		as_dict=1,
-	)
-
-
-@frappe.whitelist()
-def download_datev_csv(filters):
-	"""
-	Provide accounting entries for download in DATEV format.
-
-	Validate the filters, get the data, produce the CSV file and provide it for
-	download. Can be called like this:
-
-	GET /api/method/erpnext.regional.report.datev.datev.download_datev_csv
-
-	Arguments / Params:
-	filters -- dict of filters to be passed to the sql query
-	"""
-	if isinstance(filters, str):
-		filters = json.loads(filters)
-
-	validate(filters)
-	company = filters.get("company")
-
-	fiscal_year = get_fiscal_year(date=filters.get("from_date"), company=company)
-	filters["fiscal_year_start"] = fiscal_year[1]
-
-	# set chart of accounts used
-	coa = frappe.get_value("Company", company, "chart_of_accounts")
-	filters["skr"] = "04" if "SKR04" in coa else ("03" if "SKR03" in coa else "")
-
-	datev_settings = frappe.get_doc("DATEV Settings", company)
-	filters["account_number_length"] = datev_settings.account_number_length
-	filters["temporary_against_account_number"] = datev_settings.temporary_against_account_number
-
-	transactions = get_transactions(filters)
-	account_names = get_account_names(filters)
-	customers = get_customers(filters)
-	suppliers = get_suppliers(filters)
-
-	zip_name = "{} DATEV.zip".format(frappe.utils.datetime.date.today())
-	zip_and_download(
-		zip_name,
-		[
-			{
-				"file_name": "EXTF_Buchungsstapel.csv",
-				"csv_data": get_datev_csv(transactions, filters, csv_class=Transactions),
-			},
-			{
-				"file_name": "EXTF_Kontenbeschriftungen.csv",
-				"csv_data": get_datev_csv(account_names, filters, csv_class=AccountNames),
-			},
-			{
-				"file_name": "EXTF_Kunden.csv",
-				"csv_data": get_datev_csv(customers, filters, csv_class=DebtorsCreditors),
-			},
-			{
-				"file_name": "EXTF_Lieferanten.csv",
-				"csv_data": get_datev_csv(suppliers, filters, csv_class=DebtorsCreditors),
-			},
-		],
-	)
diff --git a/erpnext/regional/report/datev/test_datev.py b/erpnext/regional/report/datev/test_datev.py
deleted file mode 100644
index 0df8c06..0000000
--- a/erpnext/regional/report/datev/test_datev.py
+++ /dev/null
@@ -1,252 +0,0 @@
-import zipfile
-from io import BytesIO
-from unittest import TestCase
-
-import frappe
-from frappe.utils import cstr, now_datetime, today
-
-from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
-from erpnext.regional.germany.utils.datev.datev_constants import (
-	AccountNames,
-	DebtorsCreditors,
-	Transactions,
-)
-from erpnext.regional.germany.utils.datev.datev_csv import get_datev_csv, get_header
-from erpnext.regional.report.datev.datev import (
-	download_datev_csv,
-	get_account_names,
-	get_customers,
-	get_suppliers,
-	get_transactions,
-)
-
-
-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": "EUR",
-				"country": "Germany",
-				"create_chart_of_accounts_based_on": "Standard Template",
-				"chart_of_accounts": "SKR04 mit Kontonummern",
-			}
-		)
-		company.insert()
-	else:
-		company = frappe.get_doc("Company", company_name)
-
-	# indempotent
-	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 setup_fiscal_year():
-	fiscal_year = None
-	year = cstr(now_datetime().year)
-	if not frappe.db.get_value("Fiscal Year", {"year": year}, "name"):
-		try:
-			fiscal_year = frappe.get_doc(
-				{
-					"doctype": "Fiscal Year",
-					"year": year,
-					"year_start_date": "{0}-01-01".format(year),
-					"year_end_date": "{0}-12-31".format(year),
-				}
-			)
-			fiscal_year.insert()
-		except frappe.NameError:
-			pass
-
-	if fiscal_year:
-		fiscal_year.set_as_default()
-
-
-def make_customer_with_account(customer_name, company):
-	acc_name = frappe.db.get_value(
-		"Account", {"account_name": customer_name, "company": company.name}, "name"
-	)
-
-	if not acc_name:
-		acc = frappe.get_doc(
-			{
-				"doctype": "Account",
-				"parent_account": "1 - Forderungen aus Lieferungen und Leistungen - _TG",
-				"account_name": customer_name,
-				"company": company.name,
-				"account_type": "Receivable",
-				"account_number": "10001",
-			}
-		)
-		acc.insert()
-		acc_name = acc.name
-
-	if not frappe.db.exists("Customer", customer_name):
-		customer = frappe.get_doc(
-			{
-				"doctype": "Customer",
-				"customer_name": customer_name,
-				"customer_type": "Company",
-				"accounts": [{"company": company.name, "account": acc_name}],
-			}
-		)
-		customer.insert()
-	else:
-		customer = frappe.get_doc("Customer", customer_name)
-
-	return customer
-
-
-def make_item(item_code, company):
-	warehouse_name = frappe.db.get_value(
-		"Warehouse", {"warehouse_name": "Stores", "company": company.name}, "name"
-	)
-
-	if not frappe.db.exists("Item", item_code):
-		item = frappe.get_doc(
-			{
-				"doctype": "Item",
-				"item_code": item_code,
-				"item_name": item_code,
-				"description": item_code,
-				"item_group": "All Item Groups",
-				"is_stock_item": 0,
-				"is_purchase_item": 0,
-				"is_customer_provided_item": 0,
-				"item_defaults": [{"default_warehouse": warehouse_name, "company": company.name}],
-			}
-		)
-		item.insert()
-	else:
-		item = frappe.get_doc("Item", item_code)
-	return item
-
-
-def make_datev_settings(company):
-	if not frappe.db.exists("DATEV Settings", company.name):
-		frappe.get_doc(
-			{
-				"doctype": "DATEV Settings",
-				"client": company.name,
-				"client_number": "12345",
-				"consultant_number": "67890",
-				"temporary_against_account_number": "9999",
-			}
-		).insert()
-
-
-class TestDatev(TestCase):
-	def setUp(self):
-		self.company = make_company("_Test GmbH", "_TG")
-		self.customer = make_customer_with_account("_Test Kunde GmbH", self.company)
-		self.filters = {
-			"company": self.company.name,
-			"from_date": today(),
-			"to_date": today(),
-			"temporary_against_account_number": "9999",
-		}
-
-		make_datev_settings(self.company)
-		item = make_item("_Test Item", self.company)
-		setup_fiscal_year()
-
-		warehouse = frappe.db.get_value(
-			"Item Default", {"parent": item.name, "company": self.company.name}, "default_warehouse"
-		)
-
-		income_account = frappe.db.get_value(
-			"Account", {"account_number": "4200", "company": self.company.name}, "name"
-		)
-
-		tax_account = frappe.db.get_value(
-			"Account", {"account_number": "3806", "company": self.company.name}, "name"
-		)
-
-		si = create_sales_invoice(
-			company=self.company.name,
-			customer=self.customer.name,
-			currency=self.company.default_currency,
-			debit_to=self.customer.accounts[0].account,
-			income_account="4200 - Erlöse - _TG",
-			expense_account="6990 - Herstellungskosten - _TG",
-			cost_center=self.company.cost_center,
-			warehouse=warehouse,
-			item=item.name,
-			do_not_save=1,
-		)
-
-		si.append(
-			"taxes",
-			{
-				"charge_type": "On Net Total",
-				"account_head": tax_account,
-				"description": "Umsatzsteuer 19 %",
-				"rate": 19,
-				"cost_center": self.company.cost_center,
-			},
-		)
-
-		si.cost_center = self.company.cost_center
-
-		si.save()
-		si.submit()
-
-	def test_columns(self):
-		def is_subset(get_data, allowed_keys):
-			"""
-			Validate that the dict contains only allowed keys.
-
-			Params:
-			get_data -- Function that returns a list of dicts.
-			allowed_keys -- List of allowed keys
-			"""
-			data = get_data(self.filters)
-			if data == []:
-				# No data and, therefore, no columns is okay
-				return True
-			actual_set = set(data[0].keys())
-			# allowed set must be interpreted as unicode to match the actual set
-			allowed_set = set({frappe.as_unicode(key) for key in allowed_keys})
-			return actual_set.issubset(allowed_set)
-
-		self.assertTrue(is_subset(get_transactions, Transactions.COLUMNS))
-		self.assertTrue(is_subset(get_customers, DebtorsCreditors.COLUMNS))
-		self.assertTrue(is_subset(get_suppliers, DebtorsCreditors.COLUMNS))
-		self.assertTrue(is_subset(get_account_names, AccountNames.COLUMNS))
-
-	def test_header(self):
-		self.assertTrue(Transactions.DATA_CATEGORY in get_header(self.filters, Transactions))
-		self.assertTrue(AccountNames.DATA_CATEGORY in get_header(self.filters, AccountNames))
-		self.assertTrue(DebtorsCreditors.DATA_CATEGORY in get_header(self.filters, DebtorsCreditors))
-
-	def test_csv(self):
-		test_data = [
-			{
-				"Umsatz (ohne Soll/Haben-Kz)": 100,
-				"Soll/Haben-Kennzeichen": "H",
-				"Kontonummer": "4200",
-				"Gegenkonto (ohne BU-Schlüssel)": "10000",
-				"Belegdatum": today(),
-				"Buchungstext": "No remark",
-				"Beleginfo - Art 1": "Sales Invoice",
-				"Beleginfo - Inhalt 1": "SINV-0001",
-			}
-		]
-		get_datev_csv(data=test_data, filters=self.filters, csv_class=Transactions)
-
-	def test_download(self):
-		"""Assert that the returned file is a ZIP file."""
-		download_datev_csv(self.filters)
-
-		# zipfile.is_zipfile() expects a file-like object
-		zip_buffer = BytesIO()
-		zip_buffer.write(frappe.response["filecontent"])
-
-		self.assertTrue(zipfile.is_zipfile(zip_buffer))
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index dd185fc..0de5b2d 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -233,7 +233,8 @@
 		["expenses_included_in_asset_valuation", {"account_type": "Expenses Included In Asset Valuation"}],
 		["capital_work_in_progress_account", {"account_type": "Capital Work in Progress"}],
 		["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}],
-		["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}]
+		["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}],
+		["default_provisional_account", {"root_type": ["in", ["Liability", "Asset"]]}]
 	], function(i, v) {
 		erpnext.company.set_custom_query(frm, v);
 	});
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index 23301a6..ae8b488 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -377,6 +377,17 @@
 			}
 		}
 
+		frm.set_query('default_provisional_account', 'item_defaults', (doc, cdt, cdn) => {
+			let row = locals[cdt][cdn];
+			return {
+				filters: {
+					"company": row.company,
+					"root_type": ["in", ["Liability", "Asset"]],
+					"is_group": 0
+				}
+			};
+		});
+
 	},
 
 	make_dashboard: function(frm) {
diff --git a/erpnext/stock/doctype/item_default/item_default.json b/erpnext/stock/doctype/item_default/item_default.json
index bc17160..042d398 100644
--- a/erpnext/stock/doctype/item_default/item_default.json
+++ b/erpnext/stock/doctype/item_default/item_default.json
@@ -15,6 +15,7 @@
   "default_supplier",
   "column_break_8",
   "expense_account",
+  "default_provisional_account",
   "selling_defaults",
   "selling_cost_center",
   "column_break_12",
@@ -101,11 +102,17 @@
    "fieldtype": "Link",
    "label": "Default Discount Account",
    "options": "Account"
+  },
+  {
+   "fieldname": "default_provisional_account",
+   "fieldtype": "Link",
+   "label": "Default Provisional Account",
+   "options": "Account"
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2021-07-13 01:26:03.860065",
+ "modified": "2022-04-10 20:18:54.148195",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Item Default",
@@ -114,5 +121,6 @@
  "quick_entry": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ 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 6e5f6f5..19c490d 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -106,8 +106,6 @@
   "terms",
   "bill_no",
   "bill_date",
-  "accounting_details_section",
-  "provisional_expense_account",
   "more_info",
   "project",
   "status",
@@ -1145,26 +1143,13 @@
    "label": "Represents Company",
    "options": "Company",
    "read_only": 1
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "accounting_details_section",
-   "fieldtype": "Section Break",
-   "label": "Accounting Details"
-  },
-  {
-   "fieldname": "provisional_expense_account",
-   "fieldtype": "Link",
-   "hidden": 1,
-   "label": "Provisional Expense Account",
-   "options": "Account"
   }
  ],
  "icon": "fa fa-truck",
  "idx": 261,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-03-10 11:40:52.690984",
+ "modified": "2022-04-10 22:50:37.761362",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 1e1c0b9..ec0e809 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -145,10 +145,13 @@
 			)
 		)
 
-		if provisional_accounting_for_non_stock_items:
-			default_provisional_account = self.get_company_default("default_provisional_account")
-			if not self.provisional_expense_account:
-				self.provisional_expense_account = default_provisional_account
+		if not provisional_accounting_for_non_stock_items:
+			return
+
+		default_provisional_account = self.get_company_default("default_provisional_account")
+		for item in self.get("items"):
+			if not item.get("provisional_expense_account"):
+				item.provisional_expense_account = default_provisional_account
 
 	def validate_with_previous_doc(self):
 		super(PurchaseReceipt, self).validate_with_previous_doc(
@@ -509,7 +512,9 @@
 				and flt(d.qty)
 				and provisional_accounting_for_non_stock_items
 			):
-				self.add_provisional_gl_entry(d, gl_entries, self.posting_date)
+				self.add_provisional_gl_entry(
+					d, gl_entries, self.posting_date, d.get("provisional_expense_account")
+				)
 
 		if warehouse_with_no_account:
 			frappe.msgprint(
@@ -518,9 +523,10 @@
 				+ "\n".join(warehouse_with_no_account)
 			)
 
-	def add_provisional_gl_entry(self, item, gl_entries, posting_date, reverse=0):
-		provisional_expense_account = self.get("provisional_expense_account")
-		credit_currency = get_account_currency(provisional_expense_account)
+	def add_provisional_gl_entry(
+		self, item, gl_entries, posting_date, provisional_account, reverse=0
+	):
+		credit_currency = get_account_currency(provisional_account)
 		debit_currency = get_account_currency(item.expense_account)
 		expense_account = item.expense_account
 		remarks = self.get("remarks") or _("Accounting Entry for Service")
@@ -534,7 +540,7 @@
 
 		self.add_gl_entry(
 			gl_entries=gl_entries,
-			account=provisional_expense_account,
+			account=provisional_account,
 			cost_center=item.cost_center,
 			debit=0.0,
 			credit=multiplication_factor * item.amount,
@@ -554,7 +560,7 @@
 			debit=multiplication_factor * item.amount,
 			credit=0.0,
 			remarks=remarks,
-			against_account=provisional_expense_account,
+			against_account=provisional_account,
 			account_currency=debit_currency,
 			project=item.project,
 			voucher_detail_no=item.name,
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index f3faba4..ce3bd56 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -747,14 +747,13 @@
 			update_purchase_receipt_status,
 		)
 
-		pr = make_purchase_receipt()
+		item = make_item()
+
+		pr = make_purchase_receipt(item_code=item.name)
 
 		update_purchase_receipt_status(pr.name, "Closed")
 		self.assertEqual(frappe.db.get_value("Purchase Receipt", pr.name, "status"), "Closed")
 
-		pr.reload()
-		pr.cancel()
-
 	def test_pr_billing_status(self):
 		"""Flow:
 		1. PO -> PR1 -> PI
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
index 03a4201..1c65ac8 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -96,7 +96,6 @@
   "include_exploded_items",
   "batch_no",
   "rejected_serial_no",
-  "expense_account",
   "item_tax_rate",
   "item_weight_details",
   "weight_per_unit",
@@ -107,6 +106,10 @@
   "manufacturer",
   "column_break_16",
   "manufacturer_part_no",
+  "accounting_details_section",
+  "expense_account",
+  "column_break_102",
+  "provisional_expense_account",
   "accounting_dimensions_section",
   "project",
   "dimension_col_break",
@@ -971,12 +974,27 @@
    "label": "Product Bundle",
    "options": "Product Bundle",
    "read_only": 1
+  },
+  {
+   "fieldname": "provisional_expense_account",
+   "fieldtype": "Link",
+   "label": "Provisional Expense Account",
+   "options": "Account"
+  },
+  {
+   "fieldname": "accounting_details_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Details"
+  },
+  {
+   "fieldname": "column_break_102",
+   "fieldtype": "Column Break"
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2022-02-01 11:32:27.980524",
+ "modified": "2022-04-11 13:07:32.061402",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt Item",
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index d3a230e..324ff4f 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -345,6 +345,7 @@
 			"expense_account": expense_account
 			or get_default_expense_account(args, item_defaults, item_group_defaults, brand_defaults),
 			"discount_account": get_default_discount_account(args, item_defaults),
+			"provisional_expense_account": get_provisional_account(args, item_defaults),
 			"cost_center": get_default_cost_center(
 				args, item_defaults, item_group_defaults, brand_defaults
 			),
@@ -699,6 +700,10 @@
 	)
 
 
+def get_provisional_account(args, item):
+	return item.get("default_provisional_account") or args.default_provisional_account
+
+
 def get_default_discount_account(args, item):
 	return item.get("default_discount_account") or args.discount_account
 
diff --git a/erpnext/telephony/doctype/call_log/call_log.json b/erpnext/telephony/doctype/call_log/call_log.json
index 1d6c39e..a41ddb1 100644
--- a/erpnext/telephony/doctype/call_log/call_log.json
+++ b/erpnext/telephony/doctype/call_log/call_log.json
@@ -1,7 +1,7 @@
 {
  "actions": [],
  "autoname": "field:id",
- "creation": "2019-06-05 12:07:02.634534",
+ "creation": "2022-02-21 11:54:58.414784",
  "doctype": "DocType",
  "engine": "InnoDB",
  "field_order": [
@@ -9,6 +9,8 @@
   "id",
   "from",
   "to",
+  "call_received_by",
+  "employee_user_id",
   "medium",
   "start_time",
   "end_time",
@@ -20,6 +22,7 @@
   "recording_url",
   "recording_html",
   "section_break_11",
+  "type_of_call",
   "summary",
   "section_break_19",
   "links"
@@ -103,7 +106,8 @@
   },
   {
    "fieldname": "summary",
-   "fieldtype": "Small Text"
+   "fieldtype": "Small Text",
+   "label": "Summary"
   },
   {
    "fieldname": "section_break_11",
@@ -134,15 +138,37 @@
    "fieldname": "call_details_section",
    "fieldtype": "Section Break",
    "label": "Call Details"
+  },
+  {
+   "fieldname": "employee_user_id",
+   "fieldtype": "Link",
+   "hidden": 1,
+   "label": "Employee User Id",
+   "options": "User"
+  },
+  {
+   "fieldname": "type_of_call",
+   "fieldtype": "Link",
+   "label": "Type Of Call",
+   "options": "Telephony Call Type"
+  },
+  {
+   "depends_on": "to",
+   "fieldname": "call_received_by",
+   "fieldtype": "Link",
+   "label": "Call Received By",
+   "options": "Employee",
+   "read_only": 1
   }
  ],
  "in_create": 1,
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2021-02-08 14:23:28.744844",
+ "modified": "2022-04-14 02:59:22.503202",
  "modified_by": "Administrator",
  "module": "Telephony",
  "name": "Call Log",
+ "naming_rule": "By fieldname",
  "owner": "Administrator",
  "permissions": [
   {
@@ -164,6 +190,7 @@
  ],
  "sort_field": "creation",
  "sort_order": "DESC",
+ "states": [],
  "title_field": "from",
  "track_changes": 1,
  "track_views": 1
diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py
index 1c88883..7725e71 100644
--- a/erpnext/telephony/doctype/call_log/call_log.py
+++ b/erpnext/telephony/doctype/call_log/call_log.py
@@ -32,6 +32,10 @@
 		if lead:
 			self.add_link(link_type="Lead", link_name=lead)
 
+		# Add Employee Name
+		if self.is_incoming_call():
+			self.update_received_by()
+
 	def after_insert(self):
 		self.trigger_call_popup()
 
@@ -49,6 +53,9 @@
 		if not doc_before_save:
 			return
 
+		if self.is_incoming_call() and self.has_value_changed("to"):
+			self.update_received_by()
+
 		if _is_call_missed(doc_before_save, self):
 			frappe.publish_realtime("call_{id}_missed".format(id=self.id), self)
 			self.trigger_call_popup()
@@ -65,7 +72,8 @@
 	def trigger_call_popup(self):
 		if self.is_incoming_call():
 			scheduled_employees = get_scheduled_employees_for_popup(self.medium)
-			employee_emails = get_employees_with_number(self.to)
+			employees = get_employees_with_number(self.to)
+			employee_emails = [employee.get("user_id") for employee in employees]
 
 			# check if employees with matched number are scheduled to receive popup
 			emails = set(scheduled_employees).intersection(employee_emails)
@@ -85,10 +93,17 @@
 			for email in emails:
 				frappe.publish_realtime("show_call_popup", self, user=email)
 
+	def update_received_by(self):
+		if employees := get_employees_with_number(self.get("to")):
+			self.call_received_by = employees[0].get("name")
+			self.employee_user_id = employees[0].get("user_id")
+
 
 @frappe.whitelist()
-def add_call_summary(call_log, summary):
+def add_call_summary_and_call_type(call_log, summary, call_type):
 	doc = frappe.get_doc("Call Log", call_log)
+	doc.type_of_call = call_type
+	doc.save()
 	doc.add_comment("Comment", frappe.bold(_("Call Summary")) + "<br><br>" + summary)
 
 
@@ -97,20 +112,19 @@
 	if not number:
 		return []
 
-	employee_emails = frappe.cache().hget("employees_with_number", number)
-	if employee_emails:
-		return employee_emails
+	employee_doc_name_and_emails = frappe.cache().hget("employees_with_number", number)
+	if employee_doc_name_and_emails:
+		return employee_doc_name_and_emails
 
-	employees = frappe.get_all(
+	employee_doc_name_and_emails = frappe.get_all(
 		"Employee",
-		filters={"cell_number": ["like", "%{}%".format(number)], "user_id": ["!=", ""]},
-		fields=["user_id"],
+		filters={"cell_number": ["like", f"%{number}%"], "user_id": ["!=", ""]},
+		fields=["name", "user_id"],
 	)
 
-	employee_emails = [employee.user_id for employee in employees]
-	frappe.cache().hset("employees_with_number", number, employee_emails)
+	frappe.cache().hset("employees_with_number", number, employee_doc_name_and_emails)
 
-	return employee_emails
+	return employee_doc_name_and_emails
 
 
 def link_existing_conversations(doc, state):
diff --git a/erpnext/regional/doctype/datev_settings/__init__.py b/erpnext/telephony/doctype/telephony_call_type/__init__.py
similarity index 100%
rename from erpnext/regional/doctype/datev_settings/__init__.py
rename to erpnext/telephony/doctype/telephony_call_type/__init__.py
diff --git a/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.js b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.js
new file mode 100644
index 0000000..efba2b8
--- /dev/null
+++ b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Telephony Call Type', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.json b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.json
new file mode 100644
index 0000000..603709e
--- /dev/null
+++ b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.json
@@ -0,0 +1,58 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "field:call_type",
+ "creation": "2022-02-25 16:13:37.321312",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "call_type",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "fieldname": "call_type",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Call Type",
+   "reqd": 1,
+   "unique": 1
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Telephony Call Type",
+   "print_hide": 1,
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2022-02-25 16:14:07.087461",
+ "modified_by": "Administrator",
+ "module": "Telephony",
+ "name": "Telephony Call Type",
+ "naming_rule": "By fieldname",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.py b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.py
new file mode 100644
index 0000000..944ffef
--- /dev/null
+++ b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class TelephonyCallType(Document):
+	pass
diff --git a/erpnext/telephony/doctype/telephony_call_type/test_telephony_call_type.py b/erpnext/telephony/doctype/telephony_call_type/test_telephony_call_type.py
new file mode 100644
index 0000000..b3c19c3
--- /dev/null
+++ b/erpnext/telephony/doctype/telephony_call_type/test_telephony_call_type.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+
+class TestTelephonyCallType(unittest.TestCase):
+	pass
diff --git a/erpnext/tests/exotel_test_data.py b/erpnext/tests/exotel_test_data.py
new file mode 100644
index 0000000..3ad2575
--- /dev/null
+++ b/erpnext/tests/exotel_test_data.py
@@ -0,0 +1,122 @@
+import frappe
+
+call_initiation_data = frappe._dict(
+	{
+		"CallSid": "23c162077629863c1a2d7f29263a162m",
+		"CallFrom": "09999999991",
+		"CallTo": "09999999980",
+		"Direction": "incoming",
+		"Created": "Wed, 23 Feb 2022 12:31:59",
+		"From": "09999999991",
+		"To": "09999999988",
+		"CurrentTime": "2022-02-23 12:32:02",
+		"DialWhomNumber": "09999999999",
+		"Status": "busy",
+		"EventType": "Dial",
+		"AgentEmail": "test_employee_exotel@company.com",
+	}
+)
+
+call_end_data = frappe._dict(
+	{
+		"CallSid": "23c162077629863c1a2d7f29263a162m",
+		"CallFrom": "09999999991",
+		"CallTo": "09999999980",
+		"Direction": "incoming",
+		"ForwardedFrom": "null",
+		"Created": "Wed, 23 Feb 2022 12:31:59",
+		"DialCallDuration": "17",
+		"RecordingUrl": "https://s3-ap-southeast-1.amazonaws.com/random.mp3",
+		"StartTime": "2022-02-23 12:31:58",
+		"EndTime": "1970-01-01 05:30:00",
+		"DialCallStatus": "completed",
+		"CallType": "completed",
+		"DialWhomNumber": "09999999999",
+		"ProcessStatus": "null",
+		"flow_id": "228040",
+		"tenant_id": "67291",
+		"From": "09999999991",
+		"To": "09999999988",
+		"RecordingAvailableBy": "Wed, 23 Feb 2022 12:37:25",
+		"CurrentTime": "2022-02-23 12:32:25",
+		"OutgoingPhoneNumber": "09999999988",
+		"Legs": [
+			{
+				"Number": "09999999999",
+				"Type": "single",
+				"OnCallDuration": "10",
+				"CallerId": "09999999980",
+				"CauseCode": "NORMAL_CLEARING",
+				"Cause": "16",
+			}
+		],
+	}
+)
+
+call_disconnected_data = frappe._dict(
+	{
+		"CallSid": "d96421addce69e24bdc7ce5880d1162l",
+		"CallFrom": "09999999991",
+		"CallTo": "09999999980",
+		"Direction": "incoming",
+		"ForwardedFrom": "null",
+		"Created": "Mon, 21 Feb 2022 15:58:12",
+		"DialCallDuration": "0",
+		"StartTime": "2022-02-21 15:58:12",
+		"EndTime": "1970-01-01 05:30:00",
+		"DialCallStatus": "canceled",
+		"CallType": "client-hangup",
+		"DialWhomNumber": "09999999999",
+		"ProcessStatus": "null",
+		"flow_id": "228040",
+		"tenant_id": "67291",
+		"From": "09999999991",
+		"To": "09999999988",
+		"CurrentTime": "2022-02-21 15:58:47",
+		"OutgoingPhoneNumber": "09999999988",
+		"Legs": [
+			{
+				"Number": "09999999999",
+				"Type": "single",
+				"OnCallDuration": "0",
+				"CallerId": "09999999980",
+				"CauseCode": "RING_TIMEOUT",
+				"Cause": "1003",
+			}
+		],
+	}
+)
+
+call_not_answered_data = frappe._dict(
+	{
+		"CallSid": "fdb67a2b4b2d057b610a52ef43f81622",
+		"CallFrom": "09999999991",
+		"CallTo": "09999999980",
+		"Direction": "incoming",
+		"ForwardedFrom": "null",
+		"Created": "Mon, 21 Feb 2022 15:47:02",
+		"DialCallDuration": "0",
+		"StartTime": "2022-02-21 15:47:02",
+		"EndTime": "1970-01-01 05:30:00",
+		"DialCallStatus": "no-answer",
+		"CallType": "incomplete",
+		"DialWhomNumber": "09999999999",
+		"ProcessStatus": "null",
+		"flow_id": "228040",
+		"tenant_id": "67291",
+		"From": "09999999991",
+		"To": "09999999988",
+		"CurrentTime": "2022-02-21 15:47:40",
+		"OutgoingPhoneNumber": "09999999988",
+		"Legs": [
+			{
+				"Number": "09999999999",
+				"Type": "single",
+				"OnCallDuration": "0",
+				"CallerId": "09999999980",
+				"CauseCode": "RING_TIMEOUT",
+				"Cause": "1003",
+			}
+		],
+	}
+)
diff --git a/erpnext/tests/test_exotel.py b/erpnext/tests/test_exotel.py
new file mode 100644
index 0000000..76bbb3e
--- /dev/null
+++ b/erpnext/tests/test_exotel.py
@@ -0,0 +1,69 @@
+import frappe
+from frappe.contacts.doctype.contact.test_contact import create_contact
+from frappe.tests.test_api import FrappeAPITestCase
+
+from erpnext.hr.doctype.employee.test_employee import make_employee
+
+
+class TestExotel(FrappeAPITestCase):
+	@classmethod
+	def setUpClass(cls):
+		cls.CURRENT_DB_CONNECTION = frappe.db
+		cls.test_employee_name = make_employee(
+			user="test_employee_exotel@company.com", cell_number="9999999999"
+		)
+		frappe.db.set_value("Exotel Settings", "Exotel Settings", "enabled", 1)
+		phones = [{"phone": "+91 9999999991", "is_primary_phone": 0, "is_primary_mobile_no": 1}]
+		create_contact(name="Test Contact", salutation="Mr", phones=phones)
+		frappe.db.commit()
+
+	def test_for_successful_call(self):
+		from .exotel_test_data import call_end_data, call_initiation_data
+
+		api_method = "handle_incoming_call"
+		end_call_api_method = "handle_end_call"
+
+		self.emulate_api_call_from_exotel(api_method, call_initiation_data)
+		self.emulate_api_call_from_exotel(end_call_api_method, call_end_data)
+		call_log = frappe.get_doc("Call Log", call_initiation_data.CallSid)
+
+		self.assertEqual(call_log.get("from"), call_initiation_data.CallFrom)
+		self.assertEqual(call_log.get("to"), call_initiation_data.DialWhomNumber)
+		self.assertEqual(call_log.get("call_received_by"), self.test_employee_name)
+		self.assertEqual(call_log.get("status"), "Completed")
+
+	def test_for_disconnected_call(self):
+		from .exotel_test_data import call_disconnected_data
+
+		api_method = "handle_missed_call"
+		self.emulate_api_call_from_exotel(api_method, call_disconnected_data)
+		call_log = frappe.get_doc("Call Log", call_disconnected_data.CallSid)
+		self.assertEqual(call_log.get("from"), call_disconnected_data.CallFrom)
+		self.assertEqual(call_log.get("to"), call_disconnected_data.DialWhomNumber)
+		self.assertEqual(call_log.get("call_received_by"), self.test_employee_name)
+		self.assertEqual(call_log.get("status"), "Canceled")
+
+	def test_for_call_not_answered(self):
+		from .exotel_test_data import call_not_answered_data
+
+		api_method = "handle_missed_call"
+		self.emulate_api_call_from_exotel(api_method, call_not_answered_data)
+		call_log = frappe.get_doc("Call Log", call_not_answered_data.CallSid)
+		self.assertEqual(call_log.get("from"), call_not_answered_data.CallFrom)
+		self.assertEqual(call_log.get("to"), call_not_answered_data.DialWhomNumber)
+		self.assertEqual(call_log.get("call_received_by"), self.test_employee_name)
+		self.assertEqual(call_log.get("status"), "No Answer")
+
+	def emulate_api_call_from_exotel(self, api_method, data):
+		self.post(
+			f"/api/method/erpnext.erpnext_integrations.exotel_integration.{api_method}",
+			data=frappe.as_json(data),
+			content_type="application/json",
+			as_tuple=True,
+		)
+		# restart db connection to get latest data
+		frappe.connect()
+
+	@classmethod
+	def tearDownClass(cls):
+		frappe.db = cls.CURRENT_DB_CONNECTION
diff --git a/requirements.txt b/requirements.txt
index 657054f..85ff515 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,6 @@
 # frappe   # https://github.com/frappe/frappe is installed during bench-init
 gocardless-pro~=1.22.0
 googlemaps
-pandas>=1.1.5,<2.0.0
 plaid-python~=7.2.1
 pycountry~=20.7.3
 PyGithub~=1.55