Merge remote-tracking branch 'upstream/develop' into feat/so-po-advance-payment-status
diff --git a/.github/helper/install.sh b/.github/helper/install.sh
index d1a97f8..915a463 100644
--- a/.github/helper/install.sh
+++ b/.github/helper/install.sh
@@ -4,7 +4,9 @@
 
 cd ~ || exit
 
-sudo apt update && sudo apt install redis-server libcups2-dev
+sudo apt update
+sudo apt remove mysql-server mysql-client
+sudo apt install libcups2-dev redis-server mariadb-client-10.6
 
 pip install frappe-bench
 
@@ -25,14 +27,14 @@
 
 
 if [ "$DB" == "mariadb" ];then
-    mysql --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'"
-    mysql --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"
+    mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'"
+    mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"
 
-    mysql --host 127.0.0.1 --port 3306 -u root -proot -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'"
-    mysql --host 127.0.0.1 --port 3306 -u root -proot -e "CREATE DATABASE test_frappe"
-    mysql --host 127.0.0.1 --port 3306 -u root -proot -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'"
+    mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'"
+    mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "CREATE DATABASE test_frappe"
+    mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'"
 
-    mysql --host 127.0.0.1 --port 3306 -u root -proot -e "FLUSH PRIVILEGES"
+    mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "FLUSH PRIVILEGES"
 fi
 
 if [ "$DB" == "postgres" ];then
diff --git a/.github/workflows/initiate_release.yml b/.github/workflows/initiate_release.yml
index ee60bad..e51c194 100644
--- a/.github/workflows/initiate_release.yml
+++ b/.github/workflows/initiate_release.yml
@@ -15,7 +15,7 @@
     strategy:
       fail-fast: false
       matrix:
-        version: ["13", "14"]
+        version: ["13", "14", "15"]
 
     steps:
       - uses: octokit/request-action@v2.x
@@ -30,23 +30,3 @@
           head: version-${{ matrix.version }}-hotfix
         env:
           GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
-
-  beta-release:
-    name: Release
-    runs-on: ubuntu-latest
-    strategy:
-      fail-fast: false
-
-    steps:
-      - uses: octokit/request-action@v2.x
-        with:
-          route: POST /repos/{owner}/{repo}/pulls
-          owner: frappe
-          repo: erpnext
-          title: |-
-            "chore: release v15 beta"
-          body: "Automated beta release."
-          base: version-15-beta
-          head: develop
-        env:
-          GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml
index 07b8de7..3514f0d 100644
--- a/.github/workflows/patch.yml
+++ b/.github/workflows/patch.yml
@@ -28,7 +28,7 @@
           MARIADB_ROOT_PASSWORD: 'root'
         ports:
           - 3306:3306
-        options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
+        options: --health-cmd="mariadb-admin ping" --health-interval=5s --health-timeout=2s --health-retries=3
 
     steps:
       - name: Clone
@@ -134,6 +134,7 @@
           }
 
           update_to_version 14
+          update_to_version 15
 
           echo "Updating to latest version"
           git -C "apps/frappe" checkout -q -f "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}"
diff --git a/.github/workflows/server-tests-mariadb.yml b/.github/workflows/server-tests-mariadb.yml
index 559be06..ccdfc8c 100644
--- a/.github/workflows/server-tests-mariadb.yml
+++ b/.github/workflows/server-tests-mariadb.yml
@@ -47,7 +47,7 @@
           MARIADB_ROOT_PASSWORD: 'root'
         ports:
           - 3306:3306
-        options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
+        options: --health-cmd="mariadb-admin ping" --health-interval=5s --health-timeout=2s --health-retries=3
 
     steps:
       - name: Clone
diff --git a/.mergify.yml b/.mergify.yml
index 804b27d..5359606 100644
--- a/.mergify.yml
+++ b/.mergify.yml
@@ -17,6 +17,7 @@
           - base=version-12
           - base=version-14
           - base=version-15
+          - base=version-16
     actions:
       close:
       comment:
@@ -24,16 +25,6 @@
             @{{author}}, thanks for the contribution, but we do not accept pull requests on a stable branch. Please raise PR on an appropriate hotfix branch.
             https://github.com/frappe/erpnext/wiki/Pull-Request-Checklist#which-branch
 
-  - name: Auto-close PRs on pre-release branch
-    conditions:
-      - base=version-13-pre-release
-    actions:
-      close:
-      comment:
-          message: |
-            @{{author}}, pre-release branch is not maintained anymore. Releases are directly done by merging hotfix branch to stable branches.
-
-
   - name: backport to develop
     conditions:
       - label="backport develop"
@@ -54,13 +45,13 @@
         assignees:
           - "{{ author }}"
 
-  - name: backport to version-14-pre-release
+  - name: backport to version-15-hotfix
     conditions:
-      - label="backport version-14-pre-release"
+      - label="backport version-15-hotfix"
     actions:
       backport:
         branches:
-          - version-14-pre-release
+          - version-15-hotfix
         assignees:
           - "{{ author }}"
 
@@ -74,35 +65,6 @@
         assignees:
           - "{{ author }}"
 
-  - name: backport to version-13-pre-release
-    conditions:
-      - label="backport version-13-pre-release"
-    actions:
-      backport:
-        branches:
-          - version-13-pre-release
-        assignees:
-          - "{{ author }}"
-
-  - name: backport to version-12-hotfix
-    conditions:
-      - label="backport version-12-hotfix"
-    actions:
-      backport:
-        branches:
-          - version-12-hotfix
-        assignees:
-          - "{{ author }}"
-
-  - name: backport to version-12-pre-release
-    conditions:
-      - label="backport version-12-pre-release"
-    actions:
-      backport:
-        branches:
-          - version-12-pre-release
-        assignees:
-          - "{{ author }}"
 
   - name: Automatic merge on CI success and review
     conditions:
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/ni_catalogo_de_cuentas.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/ni_catalogo_de_cuentas.json
index e8402d6..73ac4ab 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/ni_catalogo_de_cuentas.json
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/ni_catalogo_de_cuentas.json
@@ -1,6 +1,6 @@
 {
     "country_code": "ni", 
-    "name": "Nicaragua - Catalogo de Cuentas", 
+    "name": "Nicaragua - Catálogo de Cuentas", 
     "tree": {
         "Activo": {
             "Activo Corriente": {
@@ -491,4 +491,4 @@
             "root_type": "Liability"
         }
     }
-}
\ No newline at end of file
+}
diff --git a/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py b/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py
index e75af70..d06bd83 100644
--- a/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py
+++ b/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py
@@ -37,6 +37,7 @@
 			}
 		)
 		cle.flags.ignore_permissions = True
+		cle.flags.ignore_links = True
 		cle.submit()
 
 
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index 3a2c3cb..8f76492 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -302,3 +302,30 @@
 		default_dimensions_map[dimension.company][dimension.fieldname] = dimension.default_dimension
 
 	return dimension_filters, default_dimensions_map
+
+
+def create_accounting_dimensions_for_doctype(doctype):
+	accounting_dimensions = frappe.db.get_all(
+		"Accounting Dimension", fields=["fieldname", "label", "document_type", "disabled"]
+	)
+
+	if not accounting_dimensions:
+		return
+
+	for d in accounting_dimensions:
+		field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname})
+
+		if field:
+			continue
+
+		df = {
+			"fieldname": d.fieldname,
+			"label": d.label,
+			"fieldtype": "Link",
+			"options": d.document_type,
+			"insert_after": "accounting_dimensions_section",
+		}
+
+		create_custom_field(doctype, df, ignore_validate=True)
+
+	frappe.clear_cache(doctype=doctype)
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index 6857ba3..061bab3 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -32,6 +32,7 @@
   "column_break_19",
   "add_taxes_from_item_tax_template",
   "book_tax_discount_loss",
+  "round_row_wise_tax",
   "print_settings",
   "show_inclusive_tax_in_print",
   "show_taxes_as_table_in_print",
@@ -414,6 +415,13 @@
    "fieldname": "ignore_account_closing_balance",
    "fieldtype": "Check",
    "label": "Ignore Account Closing Balance"
+  },
+  {
+   "default": "0",
+   "description": "Tax Amount will be rounded on a row(items) level",
+   "fieldname": "round_row_wise_tax",
+   "fieldtype": "Check",
+   "label": "Round Tax Amount Row-wise"
   }
  ],
  "icon": "icon-cog",
@@ -421,7 +429,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2023-07-27 15:05:34.000264",
+ "modified": "2023-08-28 00:12:02.740633",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounts Settings",
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
index 9a7a9a3..7e2f763 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
@@ -18,6 +18,7 @@
 	get_entries,
 )
 from erpnext.accounts.utils import get_account_currency, get_balance_on
+from erpnext.setup.utils import get_exchange_rate
 
 
 class BankReconciliationTool(Document):
@@ -130,7 +131,7 @@
 	bank_transaction = frappe.db.get_values(
 		"Bank Transaction",
 		bank_transaction_name,
-		fieldname=["name", "deposit", "withdrawal", "bank_account"],
+		fieldname=["name", "deposit", "withdrawal", "bank_account", "currency"],
 		as_dict=True,
 	)[0]
 	company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
@@ -144,29 +145,94 @@
 			)
 
 	company = frappe.get_value("Account", company_account, "company")
+	company_default_currency = frappe.get_cached_value("Company", company, "default_currency")
+	company_account_currency = frappe.get_cached_value("Account", company_account, "account_currency")
+	second_account_currency = frappe.get_cached_value("Account", second_account, "account_currency")
+
+	# determine if multi-currency Journal or not
+	is_multi_currency = (
+		True
+		if company_default_currency != company_account_currency
+		or company_default_currency != second_account_currency
+		or company_default_currency != bank_transaction.currency
+		else False
+	)
 
 	accounts = []
-	# Multi Currency?
-	accounts.append(
-		{
-			"account": second_account,
-			"credit_in_account_currency": bank_transaction.deposit,
-			"debit_in_account_currency": bank_transaction.withdrawal,
-			"party_type": party_type,
-			"party": party,
-			"cost_center": get_default_cost_center(company),
-		}
-	)
+	second_account_dict = {
+		"account": second_account,
+		"account_currency": second_account_currency,
+		"credit_in_account_currency": bank_transaction.deposit,
+		"debit_in_account_currency": bank_transaction.withdrawal,
+		"party_type": party_type,
+		"party": party,
+		"cost_center": get_default_cost_center(company),
+	}
 
-	accounts.append(
-		{
-			"account": company_account,
-			"bank_account": bank_transaction.bank_account,
-			"credit_in_account_currency": bank_transaction.withdrawal,
-			"debit_in_account_currency": bank_transaction.deposit,
-			"cost_center": get_default_cost_center(company),
-		}
-	)
+	company_account_dict = {
+		"account": company_account,
+		"account_currency": company_account_currency,
+		"bank_account": bank_transaction.bank_account,
+		"credit_in_account_currency": bank_transaction.withdrawal,
+		"debit_in_account_currency": bank_transaction.deposit,
+		"cost_center": get_default_cost_center(company),
+	}
+
+	# convert transaction amount to company currency
+	if is_multi_currency:
+		exc_rate = get_exchange_rate(bank_transaction.currency, company_default_currency, posting_date)
+		withdrawal_in_company_currency = flt(exc_rate * abs(bank_transaction.withdrawal))
+		deposit_in_company_currency = flt(exc_rate * abs(bank_transaction.deposit))
+	else:
+		withdrawal_in_company_currency = bank_transaction.withdrawal
+		deposit_in_company_currency = bank_transaction.deposit
+
+	# if second account is of foreign currency, convert and set debit and credit fields.
+	if second_account_currency != company_default_currency:
+		exc_rate = get_exchange_rate(second_account_currency, company_default_currency, posting_date)
+		second_account_dict.update(
+			{
+				"exchange_rate": exc_rate,
+				"credit": deposit_in_company_currency,
+				"debit": withdrawal_in_company_currency,
+				"credit_in_account_currency": flt(deposit_in_company_currency / exc_rate) or 0,
+				"debit_in_account_currency": flt(withdrawal_in_company_currency / exc_rate) or 0,
+			}
+		)
+	else:
+		second_account_dict.update(
+			{
+				"exchange_rate": 1,
+				"credit": deposit_in_company_currency,
+				"debit": withdrawal_in_company_currency,
+				"credit_in_account_currency": deposit_in_company_currency,
+				"debit_in_account_currency": withdrawal_in_company_currency,
+			}
+		)
+
+	# if company account is of foreign currency, convert and set debit and credit fields.
+	if company_account_currency != company_default_currency:
+		exc_rate = get_exchange_rate(company_account_currency, company_default_currency, posting_date)
+		company_account_dict.update(
+			{
+				"exchange_rate": exc_rate,
+				"credit": withdrawal_in_company_currency,
+				"debit": deposit_in_company_currency,
+			}
+		)
+	else:
+		company_account_dict.update(
+			{
+				"exchange_rate": 1,
+				"credit": withdrawal_in_company_currency,
+				"debit": deposit_in_company_currency,
+				"credit_in_account_currency": withdrawal_in_company_currency,
+				"debit_in_account_currency": deposit_in_company_currency,
+			}
+		)
+
+	accounts.append(second_account_dict)
+	accounts.append(company_account_dict)
 
 	journal_entry_dict = {
 		"voucher_type": entry_type,
@@ -176,6 +242,9 @@
 		"cheque_no": reference_number,
 		"mode_of_payment": mode_of_payment,
 	}
+	if is_multi_currency:
+		journal_entry_dict.update({"multi_currency": True})
+
 	journal_entry = frappe.new_doc("Journal Entry")
 	journal_entry.update(journal_entry_dict)
 	journal_entry.set("accounts", accounts)
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
index a70af7a..db68dfa 100644
--- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
@@ -2,6 +2,16 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on("Bank Statement Import", {
+	onload(frm) {
+		frm.set_query("bank_account", function (doc) {
+			return {
+				filters: {
+					company: doc.company,
+				},
+			};
+		});
+	},
+
 	setup(frm) {
 		frappe.realtime.on("data_import_refresh", ({ data_import }) => {
 			frm.import_in_progress = false;
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
index 6a47562..4649d23 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
@@ -89,7 +89,6 @@
 		    - 0 > a: Error: already over-allocated
 		- clear means: set the latest transaction date as clearance date
 		"""
-		gl_bank_account = frappe.db.get_value("Bank Account", self.bank_account, "account")
 		remaining_amount = self.unallocated_amount
 		for payment_entry in self.payment_entries:
 			if payment_entry.allocated_amount == 0.0:
diff --git a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json
index 9cf2ac6..4ae8135 100644
--- a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json
+++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json
@@ -30,7 +30,8 @@
   {
    "fieldname": "posting_date",
    "fieldtype": "Date",
-   "label": "Posting Date"
+   "label": "Posting Date",
+   "search_index": 1
   },
   {
    "fieldname": "account_type",
@@ -153,7 +154,7 @@
  "in_create": 1,
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2023-06-29 12:24:20.500632",
+ "modified": "2023-10-30 16:15:00.470283",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Ledger Entry",
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
index d9f00be..fc90c3d 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -229,6 +229,7 @@
 			this.data = [];
 			const dialog = new frappe.ui.Dialog({
 				title: __("Select Difference Account"),
+				size: 'extra-large',
 				fields: [
 					{
 						fieldname: "allocation",
@@ -252,6 +253,13 @@
 							in_list_view: 1,
 							read_only: 1
 						}, {
+							fieldtype:'Date',
+							fieldname:"gain_loss_posting_date",
+							label: __("Posting Date"),
+							in_list_view: 1,
+							reqd: 1,
+						}, {
+
 							fieldtype:'Link',
 							options: 'Account',
 							in_list_view: 1,
@@ -285,6 +293,9 @@
 					args.forEach(d => {
 						frappe.model.set_value("Payment Reconciliation Allocation", d.docname,
 							"difference_account", d.difference_account);
+						frappe.model.set_value("Payment Reconciliation Allocation", d.docname,
+							"gain_loss_posting_date", d.gain_loss_posting_date);
+
 					});
 
 					this.reconcile_payment_entries();
@@ -300,6 +311,7 @@
 						'reference_name': d.reference_name,
 						'difference_amount': d.difference_amount,
 						'difference_account': d.difference_account,
+						'gain_loss_posting_date': d.gain_loss_posting_date
 					});
 				}
 			});
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index 3285a52..1626f25 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -328,6 +328,7 @@
 				res.difference_amount = self.get_difference_amount(pay, inv, res["allocated_amount"])
 				res.difference_account = default_exchange_gain_loss_account
 				res.exchange_rate = inv.get("exchange_rate")
+				res.update({"gain_loss_posting_date": pay.get("posting_date")})
 
 				if pay.get("amount") == 0:
 					entries.append(res)
@@ -434,6 +435,7 @@
 				"allocated_amount": flt(row.get("allocated_amount")),
 				"difference_amount": flt(row.get("difference_amount")),
 				"difference_account": row.get("difference_account"),
+				"difference_posting_date": row.get("gain_loss_posting_date"),
 				"cost_center": row.get("cost_center"),
 			}
 		)
diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
index 1d843ab..71bc498 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
@@ -14,6 +14,7 @@
 from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
 from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
 from erpnext.accounts.party import get_party_account
+from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
 from erpnext.stock.doctype.item.test_item import create_item
 
 test_dependencies = ["Item"]
@@ -85,26 +86,44 @@
 		self.customer5 = make_customer("_Test PR Customer 5", "EUR")
 
 	def create_account(self):
-		account_name = "Debtors EUR"
-		if not frappe.db.get_value(
-			"Account", filters={"account_name": account_name, "company": self.company}
-		):
-			acc = frappe.new_doc("Account")
-			acc.account_name = account_name
-			acc.parent_account = "Accounts Receivable - _PR"
-			acc.company = self.company
-			acc.account_currency = "EUR"
-			acc.account_type = "Receivable"
-			acc.insert()
-		else:
-			name = frappe.db.get_value(
-				"Account",
-				filters={"account_name": account_name, "company": self.company},
-				fieldname="name",
-				pluck=True,
-			)
-			acc = frappe.get_doc("Account", name)
-		self.debtors_eur = acc.name
+		accounts = [
+			{
+				"attribute": "debtors_eur",
+				"account_name": "Debtors EUR",
+				"parent_account": "Accounts Receivable - _PR",
+				"account_currency": "EUR",
+				"account_type": "Receivable",
+			},
+			{
+				"attribute": "creditors_usd",
+				"account_name": "Payable USD",
+				"parent_account": "Accounts Payable - _PR",
+				"account_currency": "USD",
+				"account_type": "Payable",
+			},
+		]
+
+		for x in accounts:
+			x = frappe._dict(x)
+			if not frappe.db.get_value(
+				"Account", filters={"account_name": x.account_name, "company": self.company}
+			):
+				acc = frappe.new_doc("Account")
+				acc.account_name = x.account_name
+				acc.parent_account = x.parent_account
+				acc.company = self.company
+				acc.account_currency = x.account_currency
+				acc.account_type = x.account_type
+				acc.insert()
+			else:
+				name = frappe.db.get_value(
+					"Account",
+					filters={"account_name": x.account_name, "company": self.company},
+					fieldname="name",
+					pluck=True,
+				)
+				acc = frappe.get_doc("Account", name)
+			setattr(self, x.attribute, acc.name)
 
 	def create_sales_invoice(
 		self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
@@ -151,6 +170,64 @@
 		payment.posting_date = posting_date
 		return payment
 
+	def create_purchase_invoice(
+		self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
+	):
+		"""
+		Helper function to populate default values in sales invoice
+		"""
+		pinv = make_purchase_invoice(
+			qty=qty,
+			rate=rate,
+			company=self.company,
+			customer=self.supplier,
+			item_code=self.item,
+			item_name=self.item,
+			cost_center=self.cost_center,
+			warehouse=self.warehouse,
+			debit_to=self.debit_to,
+			parent_cost_center=self.cost_center,
+			update_stock=0,
+			currency="INR",
+			is_pos=0,
+			is_return=0,
+			return_against=None,
+			income_account=self.income_account,
+			expense_account=self.expense_account,
+			do_not_save=do_not_save,
+			do_not_submit=do_not_submit,
+		)
+		return pinv
+
+	def create_purchase_order(
+		self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
+	):
+		"""
+		Helper function to populate default values in sales invoice
+		"""
+		pord = create_purchase_order(
+			qty=qty,
+			rate=rate,
+			company=self.company,
+			customer=self.supplier,
+			item_code=self.item,
+			item_name=self.item,
+			cost_center=self.cost_center,
+			warehouse=self.warehouse,
+			debit_to=self.debit_to,
+			parent_cost_center=self.cost_center,
+			update_stock=0,
+			currency="INR",
+			is_pos=0,
+			is_return=0,
+			return_against=None,
+			income_account=self.income_account,
+			expense_account=self.expense_account,
+			do_not_save=do_not_save,
+			do_not_submit=do_not_submit,
+		)
+		return pord
+
 	def clear_old_entries(self):
 		doctype_list = [
 			"GL Entry",
@@ -163,13 +240,11 @@
 		for doctype in doctype_list:
 			qb.from_(qb.DocType(doctype)).delete().where(qb.DocType(doctype).company == self.company).run()
 
-	def create_payment_reconciliation(self):
+	def create_payment_reconciliation(self, party_is_customer=True):
 		pr = frappe.new_doc("Payment Reconciliation")
 		pr.company = self.company
-		pr.party_type = (
-			self.party_type if hasattr(self, "party_type") and self.party_type else "Customer"
-		)
-		pr.party = self.customer
+		pr.party_type = "Customer" if party_is_customer else "Supplier"
+		pr.party = self.customer if party_is_customer else self.supplier
 		pr.receivable_payable_account = get_party_account(pr.party_type, pr.party, pr.company)
 		pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = nowdate()
 		return pr
@@ -906,9 +981,13 @@
 		self.assertEqual(pr.allocation[0].difference_amount, 0)
 
 	def test_reconciliation_purchase_invoice_against_return(self):
-		pi = make_purchase_invoice(
-			supplier="_Test Supplier USD", currency="USD", conversion_rate=50
-		).submit()
+		self.supplier = "_Test Supplier USD"
+		pi = self.create_purchase_invoice(qty=5, rate=50, do_not_submit=True)
+		pi.supplier = self.supplier
+		pi.currency = "USD"
+		pi.conversion_rate = 50
+		pi.credit_to = self.creditors_usd
+		pi.save().submit()
 
 		pi_return = frappe.get_doc(pi.as_dict())
 		pi_return.name = None
@@ -918,11 +997,12 @@
 		pi_return.items[0].qty = -pi_return.items[0].qty
 		pi_return.submit()
 
-		self.company = "_Test Company"
-		self.party_type = "Supplier"
-		self.customer = "_Test Supplier USD"
-
-		pr = self.create_payment_reconciliation()
+		pr = frappe.get_doc("Payment Reconciliation")
+		pr.company = self.company
+		pr.party_type = "Supplier"
+		pr.party = self.supplier
+		pr.receivable_payable_account = self.creditors_usd
+		pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = nowdate()
 		pr.get_unreconciled_entries()
 
 		invoices = []
@@ -931,6 +1011,7 @@
 			if invoice.invoice_number == pi.name:
 				invoices.append(invoice.as_dict())
 				break
+
 		for payment in pr.payments:
 			if payment.reference_name == pi_return.name:
 				payments.append(payment.as_dict())
@@ -941,6 +1022,121 @@
 		# Should not raise frappe.exceptions.ValidationError: Total Debit must be equal to Total Credit.
 		pr.reconcile()
 
+	def test_reconciliation_from_purchase_order_to_multiple_invoices(self):
+		"""
+		Reconciling advance payment from PO/SO to multiple invoices should not cause overallocation
+		"""
+
+		self.supplier = "_Test Supplier"
+
+		pi1 = self.create_purchase_invoice(qty=10, rate=100)
+		pi2 = self.create_purchase_invoice(qty=10, rate=100)
+		po = self.create_purchase_order(qty=20, rate=100)
+		pay = get_payment_entry(po.doctype, po.name)
+		# Overpay Puchase Order
+		pay.paid_amount = 3000
+		pay.save().submit()
+		# assert total allocated and unallocated before reconciliation
+		self.assertEqual(
+			(
+				pay.references[0].reference_doctype,
+				pay.references[0].reference_name,
+				pay.references[0].allocated_amount,
+			),
+			(po.doctype, po.name, 2000),
+		)
+		self.assertEqual(pay.total_allocated_amount, 2000)
+		self.assertEqual(pay.unallocated_amount, 1000)
+		self.assertEqual(pay.difference_amount, 0)
+
+		pr = self.create_payment_reconciliation(party_is_customer=False)
+		pr.get_unreconciled_entries()
+
+		self.assertEqual(len(pr.invoices), 2)
+		self.assertEqual(len(pr.payments), 2)
+
+		for x in pr.payments:
+			self.assertEqual((x.reference_type, x.reference_name), (pay.doctype, pay.name))
+
+		invoices = [x.as_dict() for x in pr.invoices]
+		payments = [x.as_dict() for x in pr.payments]
+		pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+		# partial allocation on pi1 and full allocate on pi2
+		pr.allocation[0].allocated_amount = 100
+		pr.reconcile()
+
+		# assert references and total allocated and unallocated amount
+		pay.reload()
+		self.assertEqual(len(pay.references), 3)
+		self.assertEqual(
+			(
+				pay.references[0].reference_doctype,
+				pay.references[0].reference_name,
+				pay.references[0].allocated_amount,
+			),
+			(po.doctype, po.name, 900),
+		)
+		self.assertEqual(
+			(
+				pay.references[1].reference_doctype,
+				pay.references[1].reference_name,
+				pay.references[1].allocated_amount,
+			),
+			(pi1.doctype, pi1.name, 100),
+		)
+		self.assertEqual(
+			(
+				pay.references[2].reference_doctype,
+				pay.references[2].reference_name,
+				pay.references[2].allocated_amount,
+			),
+			(pi2.doctype, pi2.name, 1000),
+		)
+		self.assertEqual(pay.total_allocated_amount, 2000)
+		self.assertEqual(pay.unallocated_amount, 1000)
+		self.assertEqual(pay.difference_amount, 0)
+
+		pr.get_unreconciled_entries()
+		self.assertEqual(len(pr.invoices), 1)
+		self.assertEqual(len(pr.payments), 2)
+
+		invoices = [x.as_dict() for x in pr.invoices]
+		payments = [x.as_dict() for x in pr.payments]
+		pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+		pr.reconcile()
+
+		# assert references and total allocated and unallocated amount
+		pay.reload()
+		self.assertEqual(len(pay.references), 3)
+		# PO references should be removed now
+		self.assertEqual(
+			(
+				pay.references[0].reference_doctype,
+				pay.references[0].reference_name,
+				pay.references[0].allocated_amount,
+			),
+			(pi1.doctype, pi1.name, 100),
+		)
+		self.assertEqual(
+			(
+				pay.references[1].reference_doctype,
+				pay.references[1].reference_name,
+				pay.references[1].allocated_amount,
+			),
+			(pi2.doctype, pi2.name, 1000),
+		)
+		self.assertEqual(
+			(
+				pay.references[2].reference_doctype,
+				pay.references[2].reference_name,
+				pay.references[2].allocated_amount,
+			),
+			(pi1.doctype, pi1.name, 900),
+		)
+		self.assertEqual(pay.total_allocated_amount, 2000)
+		self.assertEqual(pay.unallocated_amount, 1000)
+		self.assertEqual(pay.difference_amount, 0)
+
 
 def make_customer(customer_name, currency=None):
 	if not frappe.db.exists("Customer", customer_name):
diff --git a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json
index ec718aa..5b8556e 100644
--- a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json
+++ b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json
@@ -19,6 +19,7 @@
   "is_advance",
   "section_break_5",
   "difference_amount",
+  "gain_loss_posting_date",
   "column_break_7",
   "difference_account",
   "exchange_rate",
@@ -151,11 +152,16 @@
    "fieldtype": "Link",
    "label": "Cost Center",
    "options": "Cost Center"
+  },
+  {
+   "fieldname": "gain_loss_posting_date",
+   "fieldtype": "Date",
+   "label": "Difference Posting Date"
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2023-09-03 07:52:33.684217",
+ "modified": "2023-10-23 10:44:56.066303",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Reconciliation Allocation",
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index b690fed..f600424 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -1,13 +1,9 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
 import json
 
 import frappe
 from frappe import _
 from frappe.model.document import Document
-from frappe.utils import flt, get_url, nowdate
+from frappe.utils import flt, nowdate
 from frappe.utils.background_jobs import enqueue
 
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
@@ -20,7 +16,6 @@
 from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate
 from erpnext.accounts.party import get_party_account, get_party_bank_account
 from erpnext.accounts.utils import get_account_currency
-from erpnext.erpnext_integrations.stripe_integration import create_stripe_subscription
 from erpnext.utilities import payment_app_import_guard
 
 
@@ -412,35 +407,11 @@
 	def get_payment_success_url(self):
 		return self.payment_success_url
 
-	def on_payment_authorized(self, status=None):
-		if not status:
-			return
-
-		shopping_cart_settings = frappe.get_doc("E Commerce Settings")
-
-		if status in ["Authorized", "Completed"]:
-			redirect_to = None
-			self.set_as_paid()
-
-			# if shopping cart enabled and in session
-			if (
-				shopping_cart_settings.enabled
-				and hasattr(frappe.local, "session")
-				and frappe.local.session.user != "Guest"
-			) and self.payment_channel != "Phone":
-
-				success_url = shopping_cart_settings.payment_success_url
-				if success_url:
-					redirect_to = ({"Orders": "/orders", "Invoices": "/invoices", "My Account": "/me"}).get(
-						success_url, "/me"
-					)
-				else:
-					redirect_to = get_url("/orders/{0}".format(self.reference_name))
-
-			return redirect_to
-
 	def create_subscription(self, payment_provider, gateway_controller, data):
 		if payment_provider == "stripe":
+			with payment_app_import_guard():
+				from payments.payment_gateways.stripe_integration import create_stripe_subscription
+
 			return create_stripe_subscription(gateway_controller, data)
 
 
@@ -592,13 +563,12 @@
 
 
 def get_gateway_details(args):  # nosemgrep
-	"""return gateway and payment account of default payment gateway"""
-	if args.get("payment_gateway_account"):
-		return get_payment_gateway_account(args.get("payment_gateway_account"))
-
-	if args.order_type == "Shopping Cart":
-		payment_gateway_account = frappe.get_doc("E Commerce Settings").payment_gateway_account
-		return get_payment_gateway_account(payment_gateway_account)
+	"""
+	Return gateway and payment account of default payment gateway
+	"""
+	gateway_account = args.get("payment_gateway_account", {"is_default": 1})
+	if gateway_account:
+		return get_payment_gateway_account(gateway_account)
 
 	gateway_account = get_payment_gateway_account({"is_default": 1})
 
diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
index 00c402f..982bdc1 100644
--- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
@@ -6,6 +6,7 @@
 
 import frappe
 from frappe import _
+from frappe.utils import add_days, nowdate
 
 from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
 from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
@@ -125,70 +126,64 @@
 		self.assertEqual(inv.grand_total, 5474.0)
 
 	def test_tax_calculation_with_item_tax_template(self):
-		inv = create_pos_invoice(qty=84, rate=4.6, do_not_save=1)
-		item_row = inv.get("items")[0]
+		import json
 
-		add_items = [
-			(54, "_Test Account Excise Duty @ 12 - _TC"),
-			(288, "_Test Account Excise Duty @ 15 - _TC"),
-			(144, "_Test Account Excise Duty @ 20 - _TC"),
-			(430, "_Test Item Tax Template 1 - _TC"),
+		from erpnext.stock.get_item_details import get_item_details
+
+		# set tax template in item
+		item = frappe.get_cached_doc("Item", "_Test Item")
+		item.set(
+			"taxes",
+			[
+				{
+					"item_tax_template": "_Test Account Excise Duty @ 15 - _TC",
+					"valid_from": add_days(nowdate(), -5),
+				}
+			],
+		)
+		item.save()
+
+		# create POS invoice with item
+		pos_inv = create_pos_invoice(qty=84, rate=4.6, do_not_save=True)
+		item_details = get_item_details(
+			doc=pos_inv,
+			args={
+				"item_code": item.item_code,
+				"company": pos_inv.company,
+				"doctype": "POS Invoice",
+				"conversion_rate": 1.0,
+			},
+		)
+		tax_map = json.loads(item_details.item_tax_rate)
+		for tax in tax_map:
+			pos_inv.append(
+				"taxes",
+				{
+					"charge_type": "On Net Total",
+					"account_head": tax,
+					"rate": tax_map[tax],
+					"description": "Test",
+					"cost_center": "_Test Cost Center - _TC",
+				},
+			)
+		pos_inv.submit()
+		pos_inv.load_from_db()
+
+		# check if correct tax values are applied from tax template
+		self.assertEqual(pos_inv.net_total, 386.4)
+
+		expected_taxes = [
+			{
+				"tax_amount": 57.96,
+				"total": 444.36,
+			},
 		]
-		for qty, item_tax_template in add_items:
-			item_row_copy = copy.deepcopy(item_row)
-			item_row_copy.qty = qty
-			item_row_copy.item_tax_template = item_tax_template
-			inv.append("items", item_row_copy)
 
-		inv.append(
-			"taxes",
-			{
-				"account_head": "_Test Account Excise Duty - _TC",
-				"charge_type": "On Net Total",
-				"cost_center": "_Test Cost Center - _TC",
-				"description": "Excise Duty",
-				"doctype": "Sales Taxes and Charges",
-				"rate": 11,
-			},
-		)
-		inv.append(
-			"taxes",
-			{
-				"account_head": "_Test Account Education Cess - _TC",
-				"charge_type": "On Net Total",
-				"cost_center": "_Test Cost Center - _TC",
-				"description": "Education Cess",
-				"doctype": "Sales Taxes and Charges",
-				"rate": 0,
-			},
-		)
-		inv.append(
-			"taxes",
-			{
-				"account_head": "_Test Account S&H Education Cess - _TC",
-				"charge_type": "On Net Total",
-				"cost_center": "_Test Cost Center - _TC",
-				"description": "S&H Education Cess",
-				"doctype": "Sales Taxes and Charges",
-				"rate": 3,
-			},
-		)
-		inv.insert()
+		for i in range(len(expected_taxes)):
+			for key in expected_taxes[i]:
+				self.assertEqual(expected_taxes[i][key], pos_inv.get("taxes")[i].get(key))
 
-		self.assertEqual(inv.net_total, 4600)
-
-		self.assertEqual(inv.get("taxes")[0].tax_amount, 502.41)
-		self.assertEqual(inv.get("taxes")[0].total, 5102.41)
-
-		self.assertEqual(inv.get("taxes")[1].tax_amount, 197.80)
-		self.assertEqual(inv.get("taxes")[1].total, 5300.21)
-
-		self.assertEqual(inv.get("taxes")[2].tax_amount, 375.36)
-		self.assertEqual(inv.get("taxes")[2].total, 5675.57)
-
-		self.assertEqual(inv.grand_total, 5675.57)
-		self.assertEqual(inv.rounding_adjustment, 0.43)
-		self.assertEqual(inv.rounded_total, 5676.0)
+		self.assertEqual(pos_inv.get("base_total_taxes_and_charges"), 57.96)
 
 	def test_tax_calculation_with_multiple_items_and_discount(self):
 		inv = create_pos_invoice(qty=1, rate=75, do_not_save=True)
@@ -776,19 +771,28 @@
 		)
 
 		create_batch_item_with_batch("_BATCH ITEM Test For Reserve", "TestBatch-RS 02")
-		make_stock_entry(
+		se = make_stock_entry(
 			target="_Test Warehouse - _TC",
 			item_code="_BATCH ITEM Test For Reserve",
-			qty=20,
+			qty=30,
 			basic_rate=100,
-			batch_no="TestBatch-RS 02",
 		)
 
+		se.reload()
+
+		batch_no = get_batch_from_bundle(se.items[0].serial_and_batch_bundle)
+
+		# POS Invoice 1, for the batch without bundle
 		pos_inv1 = create_pos_invoice(
-			item="_BATCH ITEM Test For Reserve", rate=300, qty=15, batch_no="TestBatch-RS 02"
+			item="_BATCH ITEM Test For Reserve", rate=300, qty=15, do_not_save=1
 		)
+
+		pos_inv1.items[0].batch_no = batch_no
 		pos_inv1.save()
 		pos_inv1.submit()
+		pos_inv1.reload()
+
+		self.assertFalse(pos_inv1.items[0].serial_and_batch_bundle)
 
 		batches = get_auto_batch_nos(
 			frappe._dict(
@@ -797,7 +801,24 @@
 		)
 
 		for batch in batches:
-			if batch.batch_no == "TestBatch-RS 02" and batch.warehouse == "_Test Warehouse - _TC":
+			if batch.batch_no == batch_no and batch.warehouse == "_Test Warehouse - _TC":
+				self.assertEqual(batch.qty, 15)
+
+		# POS Invoice 2, for the batch with bundle
+		pos_inv2 = create_pos_invoice(
+			item="_BATCH ITEM Test For Reserve", rate=300, qty=10, batch_no=batch_no
+		)
+		pos_inv2.reload()
+		self.assertTrue(pos_inv2.items[0].serial_and_batch_bundle)
+
+		batches = get_auto_batch_nos(
+			frappe._dict(
+				{"item_code": "_BATCH ITEM Test For Reserve", "warehouse": "_Test Warehouse - _TC"}
+			)
+		)
+
+		for batch in batches:
+			if batch.batch_no == batch_no and batch.warehouse == "_Test Warehouse - _TC":
 				self.assertEqual(batch.qty, 5)
 
 	def test_pos_batch_item_qty_validation(self):
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
index d42b1e4..c161dac 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
@@ -454,7 +454,7 @@
 	except Exception as e:
 		frappe.db.rollback()
 		message_log = frappe.message_log.pop() if frappe.message_log else str(e)
-		error_message = safe_load_json(message_log)
+		error_message = get_error_message(message_log)
 
 		if closing_entry:
 			closing_entry.set_status(update=True, status="Failed")
@@ -483,7 +483,7 @@
 	except Exception as e:
 		frappe.db.rollback()
 		message_log = frappe.message_log.pop() if frappe.message_log else str(e)
-		error_message = safe_load_json(message_log)
+		error_message = get_error_message(message_log)
 
 		if closing_entry:
 			closing_entry.set_status(update=True, status="Submitted")
@@ -525,10 +525,8 @@
 		frappe.throw(_("Scheduler is inactive. Cannot enqueue job."), title=_("Scheduler Inactive"))
 
 
-def safe_load_json(message):
+def get_error_message(message) -> str:
 	try:
-		json_message = json.loads(message).get("message")
+		return message["message"]
 	except Exception:
-		json_message = message
-
-	return json_message
+		return str(message)
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index e489882..2d1f445 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -36,6 +36,7 @@
   "currency_and_price_list",
   "currency",
   "conversion_rate",
+  "use_transaction_date_exchange_rate",
   "column_break2",
   "buying_price_list",
   "price_list_currency",
@@ -1588,13 +1589,20 @@
    "label": "Repost Required",
    "options": "Account",
    "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "use_transaction_date_exchange_rate",
+   "fieldtype": "Check",
+   "label": "Use Transaction Date Exchange Rate",
+   "read_only": 1
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 204,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-10-01 21:01:47.282533",
+ "modified": "2023-10-16 16:24:51.886231",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 85ed126..e1f0f19 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -33,7 +33,7 @@
 )
 from erpnext.accounts.party import get_due_date, get_party_account
 from erpnext.accounts.utils import get_account_currency, get_fiscal_year
-from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_enabled
+from erpnext.assets.doctype.asset.asset import is_cwip_accounting_enabled
 from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
 from erpnext.buying.utils import check_on_hold_or_closed_status
 from erpnext.controllers.accounts_controller import validate_account_head
@@ -281,9 +281,6 @@
 			# in case of auto inventory accounting,
 			# expense account is always "Stock Received But Not Billed" for a stock item
 			# except opening entry, drop-ship entry and fixed asset items
-			if item.item_code:
-				asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category")
-
 			if (
 				auto_accounting_for_stock
 				and item.item_code in stock_items
@@ -350,22 +347,26 @@
 							frappe.msgprint(msg, title=_("Expense Head Changed"))
 
 						item.expense_account = stock_not_billed_account
-
-			elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
+			elif item.is_fixed_asset and item.pr_detail:
+				if not asset_received_but_not_billed:
+					asset_received_but_not_billed = self.get_company_default("asset_received_but_not_billed")
+				item.expense_account = asset_received_but_not_billed
+			elif item.is_fixed_asset:
+				account_type = (
+					"capital_work_in_progress_account"
+					if is_cwip_accounting_enabled(item.asset_category)
+					else "fixed_asset_account"
+				)
 				asset_category_account = get_asset_category_account(
-					"fixed_asset_account", item=item.item_code, company=self.company
+					account_type, item=item.item_code, company=self.company
 				)
 				if not asset_category_account:
-					form_link = get_link_to_form("Asset Category", asset_category)
+					form_link = get_link_to_form("Asset Category", item.asset_category)
 					throw(
 						_("Please set Fixed Asset Account in {} against {}.").format(form_link, self.company),
 						title=_("Missing Account"),
 					)
 				item.expense_account = asset_category_account
-			elif item.is_fixed_asset and item.pr_detail:
-				if not asset_received_but_not_billed:
-					asset_received_but_not_billed = self.get_company_default("asset_received_but_not_billed")
-				item.expense_account = asset_received_but_not_billed
 			elif not item.expense_account and for_validate:
 				throw(_("Expense account is mandatory for item {0}").format(item.item_code or item.item_name))
 
@@ -539,8 +540,9 @@
 			]
 			child_tables = {"items": ("expense_account",), "taxes": ("account_head",)}
 			self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
-			self.validate_for_repost()
-			self.db_set("repost_required", self.needs_repost)
+			if self.needs_repost:
+				self.validate_for_repost()
+				self.db_set("repost_required", self.needs_repost)
 
 	def make_gl_entries(self, gl_entries=None, from_repost=False):
 		if not gl_entries:
@@ -583,12 +585,11 @@
 
 	def get_gl_entries(self, warehouse_account=None):
 		self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
+
 		if self.auto_accounting_for_stock:
 			self.stock_received_but_not_billed = self.get_company_default("stock_received_but_not_billed")
-			self.expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
 		else:
 			self.stock_received_but_not_billed = None
-			self.expenses_included_in_valuation = None
 
 		self.negative_expense_to_be_booked = 0.0
 		gl_entries = []
@@ -597,9 +598,6 @@
 		self.make_item_gl_entries(gl_entries)
 		self.make_precision_loss_gl_entry(gl_entries)
 
-		if self.check_asset_cwip_enabled():
-			self.get_asset_gl_entry(gl_entries)
-
 		self.make_tax_gl_entries(gl_entries)
 		self.make_internal_transfer_gl_entries(gl_entries)
 
@@ -701,7 +699,11 @@
 				if item.item_code:
 					asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category")
 
-				if self.update_stock and self.auto_accounting_for_stock and item.item_code in stock_items:
+				if (
+					self.update_stock
+					and self.auto_accounting_for_stock
+					and (item.item_code in stock_items or item.is_fixed_asset)
+				):
 					# warehouse account
 					warehouse_debit_amount = self.make_stock_adjustment_entry(
 						gl_entries, item, voucher_wise_stock_value, account_currency
@@ -816,9 +818,7 @@
 							)
 						)
 
-				elif not item.is_fixed_asset or (
-					item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category)
-				):
+				else:
 					expense_account = (
 						item.expense_account
 						if (not item.enable_deferred_expense or self.is_return)
@@ -911,40 +911,6 @@
 									)
 								)
 
-					# If asset is bought through this document and not linked to PR
-					if self.update_stock and item.landed_cost_voucher_amount:
-						expenses_included_in_asset_valuation = self.get_company_default(
-							"expenses_included_in_asset_valuation"
-						)
-						# Amount added through landed-cost-voucher
-						gl_entries.append(
-							self.get_gl_dict(
-								{
-									"account": expenses_included_in_asset_valuation,
-									"against": expense_account,
-									"cost_center": item.cost_center,
-									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-									"credit": flt(item.landed_cost_voucher_amount),
-									"project": item.project or self.project,
-								},
-								item=item,
-							)
-						)
-
-						gl_entries.append(
-							self.get_gl_dict(
-								{
-									"account": expense_account,
-									"against": expenses_included_in_asset_valuation,
-									"cost_center": item.cost_center,
-									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-									"debit": flt(item.landed_cost_voucher_amount),
-									"project": item.project or self.project,
-								},
-								item=item,
-							)
-						)
-
 						# update gross amount of asset bought through this document
 						assets = frappe.db.get_all(
 							"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
@@ -969,11 +935,17 @@
 						(item.purchase_receipt, valuation_tax_accounts),
 					)
 
+					stock_rbnb = (
+						self.get_company_default("asset_received_but_not_billed")
+						if item.is_fixed_asset
+						else self.stock_received_but_not_billed
+					)
+
 					if not negative_expense_booked_in_pr:
 						gl_entries.append(
 							self.get_gl_dict(
 								{
-									"account": self.stock_received_but_not_billed,
+									"account": stock_rbnb,
 									"against": self.supplier,
 									"debit": flt(item.item_tax_amount, item.precision("item_tax_amount")),
 									"remarks": self.remarks or _("Accounting Entry for Stock"),
@@ -988,156 +960,12 @@
 							item.item_tax_amount, item.precision("item_tax_amount")
 						)
 
-	def get_asset_gl_entry(self, gl_entries):
-		arbnb_account = None
-		eiiav_account = None
-		asset_eiiav_currency = None
-
-		for item in self.get("items"):
-			if item.is_fixed_asset:
-				asset_amount = flt(item.net_amount) + flt(item.item_tax_amount / self.conversion_rate)
-				base_asset_amount = flt(item.base_net_amount + item.item_tax_amount)
-
-				item_exp_acc_type = frappe.get_cached_value("Account", item.expense_account, "account_type")
-				if not item.expense_account or item_exp_acc_type not in [
-					"Asset Received But Not Billed",
-					"Fixed Asset",
-				]:
-					if not arbnb_account:
-						arbnb_account = self.get_company_default("asset_received_but_not_billed")
-					item.expense_account = arbnb_account
-
-				if not self.update_stock:
-					arbnb_currency = get_account_currency(item.expense_account)
-					gl_entries.append(
-						self.get_gl_dict(
-							{
-								"account": item.expense_account,
-								"against": self.supplier,
-								"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
-								"debit": base_asset_amount,
-								"debit_in_account_currency": (
-									base_asset_amount if arbnb_currency == self.company_currency else asset_amount
-								),
-								"cost_center": item.cost_center,
-								"project": item.project or self.project,
-							},
-							item=item,
-						)
-					)
-
-					if item.item_tax_amount:
-						if not eiiav_account or not asset_eiiav_currency:
-							eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
-							asset_eiiav_currency = get_account_currency(eiiav_account)
-
-						gl_entries.append(
-							self.get_gl_dict(
-								{
-									"account": eiiav_account,
-									"against": self.supplier,
-									"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
-									"cost_center": item.cost_center,
-									"project": item.project or self.project,
-									"credit": item.item_tax_amount,
-									"credit_in_account_currency": (
-										item.item_tax_amount
-										if asset_eiiav_currency == self.company_currency
-										else item.item_tax_amount / self.conversion_rate
-									),
-								},
-								item=item,
-							)
-						)
-				else:
-					cwip_account = get_asset_account(
-						"capital_work_in_progress_account", asset_category=item.asset_category, company=self.company
-					)
-
-					cwip_account_currency = get_account_currency(cwip_account)
-					gl_entries.append(
-						self.get_gl_dict(
-							{
-								"account": cwip_account,
-								"against": self.supplier,
-								"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
-								"debit": base_asset_amount,
-								"debit_in_account_currency": (
-									base_asset_amount if cwip_account_currency == self.company_currency else asset_amount
-								),
-								"cost_center": self.cost_center,
-								"project": item.project or self.project,
-							},
-							item=item,
-						)
-					)
-
-					if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
-						if not eiiav_account or not asset_eiiav_currency:
-							eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
-							asset_eiiav_currency = get_account_currency(eiiav_account)
-
-						gl_entries.append(
-							self.get_gl_dict(
-								{
-									"account": eiiav_account,
-									"against": self.supplier,
-									"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
-									"cost_center": item.cost_center,
-									"credit": item.item_tax_amount,
-									"project": item.project or self.project,
-									"credit_in_account_currency": (
-										item.item_tax_amount
-										if asset_eiiav_currency == self.company_currency
-										else item.item_tax_amount / self.conversion_rate
-									),
-								},
-								item=item,
-							)
-						)
-
-					# Assets are bought through this document then it will be linked to this document
-					if flt(item.landed_cost_voucher_amount):
-						if not eiiav_account:
-							eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
-
-						gl_entries.append(
-							self.get_gl_dict(
-								{
-									"account": eiiav_account,
-									"against": cwip_account,
-									"cost_center": item.cost_center,
-									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-									"credit": flt(item.landed_cost_voucher_amount),
-									"project": item.project or self.project,
-								},
-								item=item,
-							)
-						)
-
-						gl_entries.append(
-							self.get_gl_dict(
-								{
-									"account": cwip_account,
-									"against": eiiav_account,
-									"cost_center": item.cost_center,
-									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
-									"debit": flt(item.landed_cost_voucher_amount),
-									"project": item.project or self.project,
-								},
-								item=item,
-							)
-						)
-
-					# update gross amount of assets bought through this document
-					assets = frappe.db.get_all(
-						"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
-					)
-					for asset in assets:
-						frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
-						frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate))
-
-		return gl_entries
+		assets = frappe.db.get_all(
+			"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
+		)
+		for asset in assets:
+			frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
+			frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate))
 
 	def make_stock_adjustment_entry(
 		self, gl_entries, item, voucher_wise_stock_value, account_currency
@@ -1836,6 +1664,7 @@
 					"po_detail": "purchase_order_item",
 					"material_request": "material_request",
 					"material_request_item": "material_request_item",
+					"wip_composite_asset": "wip_composite_asset",
 				},
 				"postprocess": update_item,
 				"condition": lambda doc: abs(doc.received_qty) < abs(doc.qty),
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index aa3d1b3..13593bc 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -5,7 +5,7 @@
 import unittest
 
 import frappe
-from frappe.tests.utils import change_settings
+from frappe.tests.utils import FrappeTestCase, change_settings
 from frappe.utils import add_days, cint, flt, getdate, nowdate, today
 
 import erpnext
@@ -38,7 +38,7 @@
 test_ignore = ["Serial No"]
 
 
-class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
+class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
 	@classmethod
 	def setUpClass(self):
 		unlink_payment_on_cancel_of_invoice()
@@ -48,6 +48,9 @@
 	def tearDownClass(self):
 		unlink_payment_on_cancel_of_invoice(0)
 
+	def tearDown(self):
+		frappe.db.rollback()
+
 	def test_purchase_invoice_received_qty(self):
 		"""
 		1. Test if received qty is validated against accepted + rejected
@@ -422,6 +425,7 @@
 			self.assertEqual(tax.tax_amount, expected_values[i][1])
 			self.assertEqual(tax.total, expected_values[i][2])
 
+	@change_settings("Accounts Settings", {"unlink_payment_on_cancellation_of_invoice": 1})
 	def test_purchase_invoice_with_advance(self):
 		from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
 			test_records as jv_test_records,
@@ -476,6 +480,7 @@
 			)
 		)
 
+	@change_settings("Accounts Settings", {"unlink_payment_on_cancellation_of_invoice": 1})
 	def test_invoice_with_advance_and_multi_payment_terms(self):
 		from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
 			test_records as jv_test_records,
@@ -1220,6 +1225,7 @@
 		acc_settings.submit_journal_entriessubmit_journal_entries = 0
 		acc_settings.save()
 
+	@change_settings("Accounts Settings", {"unlink_payment_on_cancellation_of_invoice": 1})
 	def test_gain_loss_with_advance_entry(self):
 		unlink_enabled = frappe.db.get_value(
 			"Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice"
@@ -1420,6 +1426,7 @@
 		)
 		frappe.db.set_value("Company", "_Test Company", "exchange_gain_loss_account", original_account)
 
+	@change_settings("Accounts Settings", {"unlink_payment_on_cancellation_of_invoice": 1})
 	def test_purchase_invoice_advance_taxes(self):
 		from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
 
@@ -1942,6 +1949,30 @@
 		self.assertEqual(po.docstatus, 1)
 		self.assertEqual(pi.docstatus, 1)
 
+	def test_default_cost_center_for_purchase(self):
+		from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
+
+		for c_center in ["_Test Cost Center Selling", "_Test Cost Center Buying"]:
+			create_cost_center(cost_center_name=c_center)
+
+		item = create_item(
+			"_Test Cost Center Item For Purchase",
+			is_stock_item=1,
+			buying_cost_center="_Test Cost Center Buying - _TC",
+			selling_cost_center="_Test Cost Center Selling - _TC",
+		)
+
+		pi = make_purchase_invoice(
+			item=item.name, qty=1, rate=1000, update_stock=True, do_not_submit=True, cost_center=""
+		)
+
+		pi.items[0].cost_center = ""
+		pi.set_missing_values()
+		pi.calculate_taxes_and_totals()
+		pi.save()
+
+		self.assertEqual(pi.items[0].cost_center, "_Test Cost Center Buying - _TC")
+
 
 def set_advance_flag(company, flag, default_account):
 	frappe.db.set_value(
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index f380825..f6d9c93 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -536,8 +536,9 @@
 				"taxes": ("account_head",),
 			}
 			self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
-			self.validate_for_repost()
-			self.db_set("repost_required", self.needs_repost)
+			if self.needs_repost:
+				self.validate_for_repost()
+				self.db_set("repost_required", self.needs_repost)
 
 	def set_paid_amount(self):
 		paid_amount = 0.0
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 8aa1f4c..21cc253 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -6,7 +6,7 @@
 
 import frappe
 from frappe.model.dynamic_links import get_dynamic_link_map
-from frappe.tests.utils import change_settings
+from frappe.tests.utils import FrappeTestCase, change_settings
 from frappe.utils import add_days, flt, getdate, nowdate, today
 
 import erpnext
@@ -45,13 +45,17 @@
 from erpnext.stock.utils import get_incoming_rate, get_stock_balance
 
 
-class TestSalesInvoice(unittest.TestCase):
+class TestSalesInvoice(FrappeTestCase):
 	def setUp(self):
 		from erpnext.stock.doctype.stock_ledger_entry.test_stock_ledger_entry import create_items
 
 		create_items(["_Test Internal Transfer Item"], uoms=[{"uom": "Box", "conversion_factor": 10}])
 		create_internal_parties()
 		setup_accounts()
+		frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", None)
+
+	def tearDown(self):
+		frappe.db.rollback()
 
 	def make(self):
 		w = frappe.copy_doc(test_records[0])
@@ -179,6 +183,7 @@
 		self.assertRaises(frappe.LinkExistsError, si.cancel)
 		unlink_payment_on_cancel_of_invoice()
 
+	@change_settings("Accounts Settings", {"unlink_payment_on_cancellation_of_invoice": 1})
 	def test_payment_entry_unlink_against_standalone_credit_note(self):
 		from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
 
@@ -511,70 +516,72 @@
 		self.assertEqual(si.grand_total, 5474.0)
 
 	def test_tax_calculation_with_item_tax_template(self):
+		import json
+
+		from erpnext.stock.get_item_details import get_item_details
+
+		# set tax template in item
+		item = frappe.get_cached_doc("Item", "_Test Item")
+		item.set(
+			"taxes",
+			[
+				{
+					"item_tax_template": "_Test Item Tax Template 1 - _TC",
+					"valid_from": add_days(nowdate(), -5),
+				}
+			],
+		)
+		item.save()
+
+		# create sales invoice with item
 		si = create_sales_invoice(qty=84, rate=4.6, do_not_save=True)
-		item_row = si.get("items")[0]
+		item_details = get_item_details(
+			doc=si,
+			args={
+				"item_code": item.item_code,
+				"company": si.company,
+				"doctype": "Sales Invoice",
+				"conversion_rate": 1.0,
+			},
+		)
+		tax_map = json.loads(item_details.item_tax_rate)
+		for tax in tax_map:
+			si.append(
+				"taxes",
+				{
+					"charge_type": "On Net Total",
+					"account_head": tax,
+					"rate": tax_map[tax],
+					"description": "Test",
+					"cost_center": "_Test Cost Center - _TC",
+				},
+			)
+		si.submit()
+		si.load_from_db()
 
-		add_items = [
-			(54, "_Test Account Excise Duty @ 12 - _TC"),
-			(288, "_Test Account Excise Duty @ 15 - _TC"),
-			(144, "_Test Account Excise Duty @ 20 - _TC"),
-			(430, "_Test Item Tax Template 1 - _TC"),
+		# check if correct tax values are applied from tax template
+		self.assertEqual(si.net_total, 386.4)
+
+		expected_taxes = [
+			{
+				"tax_amount": 19.32,
+				"total": 405.72,
+			},
+			{
+				"tax_amount": 38.64,
+				"total": 444.36,
+			},
+			{
+				"tax_amount": 57.96,
+				"total": 502.32,
+			},
 		]
-		for qty, item_tax_template in add_items:
-			item_row_copy = copy.deepcopy(item_row)
-			item_row_copy.qty = qty
-			item_row_copy.item_tax_template = item_tax_template
-			si.append("items", item_row_copy)
 
-		si.append(
-			"taxes",
-			{
-				"account_head": "_Test Account Excise Duty - _TC",
-				"charge_type": "On Net Total",
-				"cost_center": "_Test Cost Center - _TC",
-				"description": "Excise Duty",
-				"doctype": "Sales Taxes and Charges",
-				"rate": 11,
-			},
-		)
-		si.append(
-			"taxes",
-			{
-				"account_head": "_Test Account Education Cess - _TC",
-				"charge_type": "On Net Total",
-				"cost_center": "_Test Cost Center - _TC",
-				"description": "Education Cess",
-				"doctype": "Sales Taxes and Charges",
-				"rate": 0,
-			},
-		)
-		si.append(
-			"taxes",
-			{
-				"account_head": "_Test Account S&H Education Cess - _TC",
-				"charge_type": "On Net Total",
-				"cost_center": "_Test Cost Center - _TC",
-				"description": "S&H Education Cess",
-				"doctype": "Sales Taxes and Charges",
-				"rate": 3,
-			},
-		)
-		si.insert()
+		for i in range(len(expected_taxes)):
+			for key in expected_taxes[i]:
+				self.assertEqual(expected_taxes[i][key], si.get("taxes")[i].get(key))
 
-		self.assertEqual(si.net_total, 4600)
-
-		self.assertEqual(si.get("taxes")[0].tax_amount, 502.41)
-		self.assertEqual(si.get("taxes")[0].total, 5102.41)
-
-		self.assertEqual(si.get("taxes")[1].tax_amount, 197.80)
-		self.assertEqual(si.get("taxes")[1].total, 5300.21)
-
-		self.assertEqual(si.get("taxes")[2].tax_amount, 375.36)
-		self.assertEqual(si.get("taxes")[2].total, 5675.57)
-
-		self.assertEqual(si.grand_total, 5675.57)
-		self.assertEqual(si.rounding_adjustment, 0.43)
-		self.assertEqual(si.rounded_total, 5676.0)
+		self.assertEqual(si.get("base_total_taxes_and_charges"), 115.92)
 
 	def test_tax_calculation_with_multiple_items_and_discount(self):
 		si = create_sales_invoice(qty=1, rate=75, do_not_save=True)
@@ -1300,6 +1307,7 @@
 		dn.submit()
 		return dn
 
+	@change_settings("Accounts Settings", {"unlink_payment_on_cancellation_of_invoice": 1})
 	def test_sales_invoice_with_advance(self):
 		from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
 			test_records as jv_test_records,
@@ -2492,12 +2500,6 @@
 			"stock_received_but_not_billed",
 			"Stock Received But Not Billed - _TC1",
 		)
-		frappe.db.set_value(
-			"Company",
-			"_Test Company 1",
-			"expenses_included_in_valuation",
-			"Expenses Included In Valuation - _TC1",
-		)
 
 		# begin test
 		si = create_sales_invoice(
@@ -2547,6 +2549,7 @@
 		)
 
 		si = frappe.copy_doc(test_records[0])
+		si.customer = "_Test Internal Customer 3"
 		si.update_stock = 1
 		si.set_warehouse = "Finished Goods - _TC"
 		si.set_target_warehouse = "Stores - _TC"
@@ -2775,6 +2778,13 @@
 			company="_Test Company",
 		)
 
+		tds_payable_account = create_account(
+			account_name="TDS Payable",
+			account_type="Tax",
+			parent_account="Duties and Taxes - _TC",
+			company="_Test Company",
+		)
+
 		si = create_sales_invoice(parent_cost_center="Main - _TC", do_not_save=1)
 		si.apply_discount_on = "Grand Total"
 		si.additional_discount_account = additional_discount_account
@@ -3073,8 +3083,8 @@
 			si.commission_rate = commission_rate
 			self.assertRaises(frappe.ValidationError, si.save)
 
+	@change_settings("Accounts Settings", {"acc_frozen_upto": add_days(getdate(), 1)})
 	def test_sales_invoice_submission_post_account_freezing_date(self):
-		frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", add_days(getdate(), 1))
 		si = create_sales_invoice(do_not_save=True)
 		si.posting_date = add_days(getdate(), 1)
 		si.save()
@@ -3083,8 +3093,6 @@
 		si.posting_date = getdate()
 		si.submit()
 
-		frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", None)
-
 	def test_over_billing_case_against_delivery_note(self):
 		"""
 		Test a case where duplicating the item with qty = 1 in the invoice
@@ -3113,6 +3121,13 @@
 
 		frappe.db.set_single_value("Accounts Settings", "over_billing_allowance", over_billing_allowance)
 
+	@change_settings(
+		"Accounts Settings",
+		{
+			"book_deferred_entries_via_journal_entry": 1,
+			"submit_journal_entries": 1,
+		},
+	)
 	def test_multi_currency_deferred_revenue_via_journal_entry(self):
 		deferred_account = create_account(
 			account_name="Deferred Revenue",
@@ -3120,11 +3135,6 @@
 			company="_Test Company",
 		)
 
-		acc_settings = frappe.get_single("Accounts Settings")
-		acc_settings.book_deferred_entries_via_journal_entry = 1
-		acc_settings.submit_journal_entries = 1
-		acc_settings.save()
-
 		item = create_item("_Test Item for Deferred Accounting")
 		item.enable_deferred_expense = 1
 		item.item_defaults[0].deferred_revenue_account = deferred_account
@@ -3190,13 +3200,6 @@
 			self.assertEqual(expected_gle[i][2], gle.debit)
 			self.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
 
-		acc_settings = frappe.get_single("Accounts Settings")
-		acc_settings.book_deferred_entries_via_journal_entry = 0
-		acc_settings.submit_journal_entries = 0
-		acc_settings.save()
-
-		frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", None)
-
 	def test_standalone_serial_no_return(self):
 		si = create_sales_invoice(
 			item_code="_Test Serialized Item With Series", update_stock=True, is_return=True, qty=-1
@@ -3690,6 +3693,20 @@
 		allowed_to_interact_with="_Test Company with perpetual inventory",
 	)
 
+	create_internal_customer(
+		customer_name="_Test Internal Customer 3",
+		represents_company="_Test Company",
+		allowed_to_interact_with="_Test Company",
+	)
+
+	account = create_account(
+		account_name="Unrealized Profit",
+		parent_account="Current Liabilities - _TC",
+		company="_Test Company",
+	)
+
+	frappe.db.set_value("Company", "_Test Company", "unrealized_profit_loss_account", account)
+
 	create_internal_supplier(
 		supplier_name="_Test Internal Supplier",
 		represents_company="Wind Power LLC",
diff --git a/erpnext/accounts/doctype/subscription/subscription.js b/erpnext/accounts/doctype/subscription/subscription.js
index ae789b5..92f8a3a 100644
--- a/erpnext/accounts/doctype/subscription/subscription.js
+++ b/erpnext/accounts/doctype/subscription/subscription.js
@@ -18,6 +18,14 @@
 				}
 			};
 		});
+
+		frm.set_query('sales_tax_template', function () {
+			return {
+				filters: {
+					company: frm.doc.company
+				}
+			};
+		});
 	},
 
 	refresh: function (frm) {
diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py
index 803e879..785fd04 100644
--- a/erpnext/accounts/doctype/subscription/test_subscription.py
+++ b/erpnext/accounts/doctype/subscription/test_subscription.py
@@ -4,6 +4,7 @@
 import unittest
 
 import frappe
+from frappe.tests.utils import FrappeTestCase
 from frappe.utils.data import (
 	add_days,
 	add_months,
@@ -21,11 +22,15 @@
 test_dependencies = ("UOM", "Item Group", "Item")
 
 
-class TestSubscription(unittest.TestCase):
+class TestSubscription(FrappeTestCase):
 	def setUp(self):
 		make_plans()
 		create_parties()
 		reset_settings()
+		frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", None)
+
+	def tearDown(self):
+		frappe.db.rollback()
 
 	def test_create_subscription_with_trial_with_correct_period(self):
 		subscription = create_subscription(
diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py
index 75223c2..f6e5c56 100644
--- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py
+++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py
@@ -22,7 +22,7 @@
 
 @frappe.whitelist()
 def get_plan_rate(
-	plan, quantity=1, customer=None, start_date=None, end_date=None, prorate_factor=1
+	plan, quantity=1, customer=None, start_date=None, end_date=None, prorate_factor=1, party=None
 ):
 	plan = frappe.get_doc("Subscription Plan", plan)
 	if plan.price_determination == "Fixed Rate":
@@ -40,6 +40,7 @@
 			customer_group=customer_group,
 			company=None,
 			qty=quantity,
+			party=party,
 		)
 		if not price:
 			return 0
diff --git a/erpnext/accounts/doctype/tax_rule/tax_rule.py b/erpnext/accounts/doctype/tax_rule/tax_rule.py
index 87c5e6d..ac0dd51 100644
--- a/erpnext/accounts/doctype/tax_rule/tax_rule.py
+++ b/erpnext/accounts/doctype/tax_rule/tax_rule.py
@@ -8,7 +8,7 @@
 from frappe import _
 from frappe.contacts.doctype.address.address import get_default_address
 from frappe.model.document import Document
-from frappe.utils import cint, cstr
+from frappe.utils import cstr
 from frappe.utils.nestedset import get_root_of
 
 from erpnext.setup.doctype.customer_group.customer_group import get_parent_customer_groups
@@ -34,7 +34,6 @@
 		self.validate_tax_template()
 		self.validate_from_to_dates("from_date", "to_date")
 		self.validate_filters()
-		self.validate_use_for_shopping_cart()
 
 	def validate_tax_template(self):
 		if self.tax_type == "Sales":
@@ -106,21 +105,6 @@
 			if tax_rule[0].priority == self.priority:
 				frappe.throw(_("Tax Rule Conflicts with {0}").format(tax_rule[0].name), ConflictingTaxRule)
 
-	def validate_use_for_shopping_cart(self):
-		"""If shopping cart is enabled and no tax rule exists for shopping cart, enable this one"""
-		if (
-			not self.use_for_shopping_cart
-			and cint(frappe.db.get_single_value("E Commerce Settings", "enabled"))
-			and not frappe.db.get_value("Tax Rule", {"use_for_shopping_cart": 1, "name": ["!=", self.name]})
-		):
-
-			self.use_for_shopping_cart = 1
-			frappe.msgprint(
-				_(
-					"Enabling 'Use for Shopping Cart', as Shopping Cart is enabled and there should be at least one Tax Rule for Shopping Cart"
-				)
-			)
-
 
 @frappe.whitelist()
 def get_party_details(party, party_type, args=None):
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index d496778..70a8470 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -41,7 +41,7 @@
 					from_repost=from_repost,
 				)
 				save_entries(gl_map, adv_adj, update_outstanding, from_repost)
-			# Post GL Map proccess there may no be any GL Entries
+			# Post GL Map process there may no be any GL Entries
 			elif gl_map:
 				frappe.throw(
 					_(
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index b99bb83..16e73ea 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -5,13 +5,8 @@
 from typing import Optional
 
 import frappe
-from frappe import _, msgprint, scrub
-from frappe.contacts.doctype.address.address import (
-	get_address_display,
-	get_company_address,
-	get_default_address,
-)
-from frappe.contacts.doctype.contact.contact import get_contact_details
+from frappe import _, msgprint, qb, scrub
+from frappe.contacts.doctype.address.address import get_company_address, get_default_address
 from frappe.core.doctype.user_permission.user_permission import get_permitted_documents
 from frappe.model.utils import get_fetch_values
 from frappe.query_builder.functions import Abs, Date, Sum
@@ -133,6 +128,7 @@
 		party_address,
 		company_address,
 		shipping_address,
+		ignore_permissions=ignore_permissions,
 	)
 	set_contact_details(party_details, party, party_type)
 	set_other_values(party_details, party, party_type)
@@ -193,6 +189,8 @@
 	party_address=None,
 	company_address=None,
 	shipping_address=None,
+	*,
+	ignore_permissions=False
 ):
 	billing_address_field = (
 		"customer_address" if party_type == "Lead" else party_type.lower() + "_address"
@@ -205,13 +203,17 @@
 			get_fetch_values(doctype, billing_address_field, party_details[billing_address_field])
 		)
 	# address display
-	party_details.address_display = get_address_display(party_details[billing_address_field])
+	party_details.address_display = render_address(
+		party_details[billing_address_field], check_permissions=not ignore_permissions
+	)
 	# shipping address
 	if party_type in ["Customer", "Lead"]:
 		party_details.shipping_address_name = shipping_address or get_party_shipping_address(
 			party_type, party.name
 		)
-		party_details.shipping_address = get_address_display(party_details["shipping_address_name"])
+		party_details.shipping_address = render_address(
+			party_details["shipping_address_name"], check_permissions=not ignore_permissions
+		)
 		if doctype:
 			party_details.update(
 				get_fetch_values(doctype, "shipping_address_name", party_details.shipping_address_name)
@@ -229,7 +231,7 @@
 		if shipping_address:
 			party_details.update(
 				shipping_address=shipping_address,
-				shipping_address_display=get_address_display(shipping_address),
+				shipping_address_display=render_address(shipping_address),
 				**get_fetch_values(doctype, "shipping_address", shipping_address)
 			)
 
@@ -238,7 +240,8 @@
 			party_details.update(
 				billing_address=party_details.company_address,
 				billing_address_display=(
-					party_details.company_address_display or get_address_display(party_details.company_address)
+					party_details.company_address_display
+					or render_address(party_details.company_address, check_permissions=False)
 				),
 				**get_fetch_values(doctype, "billing_address", party_details.company_address)
 			)
@@ -290,7 +293,21 @@
 			}
 		)
 	else:
-		party_details.update(get_contact_details(party_details.contact_person))
+		fields = [
+			"name as contact_person",
+			"full_name as contact_display",
+			"email_id as contact_email",
+			"mobile_no as contact_mobile",
+			"phone as contact_phone",
+			"designation as contact_designation",
+			"department as contact_department",
+		]
+
+		contact_details = frappe.db.get_value(
+			"Contact", party_details.contact_person, fields, as_dict=True
+		)
+
+		party_details.update(contact_details)
 
 
 def set_other_values(party_details, party, party_type):
@@ -463,11 +480,19 @@
 
 def get_party_gle_currency(party_type, party, company):
 	def generator():
-		existing_gle_currency = frappe.db.sql(
-			"""select account_currency from `tabGL Entry`
-			where docstatus=1 and company=%(company)s and party_type=%(party_type)s and party=%(party)s
-			limit 1""",
-			{"company": company, "party_type": party_type, "party": party},
+		gl = qb.DocType("GL Entry")
+		existing_gle_currency = (
+			qb.from_(gl)
+			.select(gl.account_currency)
+			.where(
+				(gl.docstatus == 1)
+				& (gl.company == company)
+				& (gl.party_type == party_type)
+				& (gl.party == party)
+				& (gl.is_cancelled == 0)
+			)
+			.limit(1)
+			.run()
 		)
 
 		return existing_gle_currency[0][0] if existing_gle_currency else None
@@ -995,3 +1020,13 @@
 		doc.append("accounts", accounts)
 
 		doc.save()
+
+
+def render_address(address, check_permissions=True):
+	try:
+		from frappe.contacts.doctype.address.address import render_address as _render
+	except ImportError:
+		# Older frappe versions where this function is not available
+		from frappe.contacts.doctype.address.address import get_address_display as _render
+
+	return frappe.call(_render, address, check_permissions=check_permissions)
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index e3b671f..b9c7a0b 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -116,7 +116,7 @@
 		# build all keys, since we want to exclude vouchers beyond the report date
 		for ple in self.ple_entries:
 			# get the balance object for voucher_type
-			key = (ple.voucher_type, ple.voucher_no, ple.party)
+			key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party)
 			if not key in self.voucher_balance:
 				self.voucher_balance[key] = frappe._dict(
 					voucher_type=ple.voucher_type,
@@ -183,7 +183,7 @@
 			):
 				return
 
-		key = (ple.against_voucher_type, ple.against_voucher_no, ple.party)
+		key = (ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party)
 
 		# If payment is made against credit note
 		# and credit note is made against a Sales Invoice
@@ -192,13 +192,13 @@
 			if ple.against_voucher_no in self.return_entries:
 				return_against = self.return_entries.get(ple.against_voucher_no)
 				if return_against:
-					key = (ple.against_voucher_type, return_against, ple.party)
+					key = (ple.account, ple.against_voucher_type, return_against, ple.party)
 
 		row = self.voucher_balance.get(key)
 
 		if not row:
 			# no invoice, this is an invoice / stand-alone payment / credit note
-			row = self.voucher_balance.get((ple.voucher_type, ple.voucher_no, ple.party))
+			row = self.voucher_balance.get((ple.account, ple.voucher_type, ple.voucher_no, ple.party))
 
 		row.party_type = ple.party_type
 		return row
diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
index 4307689..cbeb6d3 100644
--- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
@@ -1,6 +1,7 @@
 import unittest
 
 import frappe
+from frappe import qb
 from frappe.tests.utils import FrappeTestCase, change_settings
 from frappe.utils import add_days, flt, getdate, today
 
@@ -23,29 +24,6 @@
 	def tearDown(self):
 		frappe.db.rollback()
 
-	def create_usd_account(self):
-		name = "Debtors USD"
-		exists = frappe.db.get_list(
-			"Account", filters={"company": "_Test Company 2", "account_name": "Debtors USD"}
-		)
-		if exists:
-			self.debtors_usd = exists[0].name
-		else:
-			debtors = frappe.get_doc(
-				"Account",
-				frappe.db.get_list(
-					"Account", filters={"company": "_Test Company 2", "account_name": "Debtors"}
-				)[0].name,
-			)
-
-			debtors_usd = frappe.new_doc("Account")
-			debtors_usd.company = debtors.company
-			debtors_usd.account_name = "Debtors USD"
-			debtors_usd.account_currency = "USD"
-			debtors_usd.parent_account = debtors.parent_account
-			debtors_usd.account_type = debtors.account_type
-			self.debtors_usd = debtors_usd.save().name
-
 	def create_sales_invoice(self, no_payment_schedule=False, do_not_submit=False):
 		frappe.set_user("Administrator")
 		si = create_sales_invoice(
@@ -643,3 +621,94 @@
 		self.assertEqual(len(report[1]), 2)
 		output_for = set([x.party for x in report[1]])
 		self.assertEqual(output_for, expected_output)
+
+	def test_report_output_if_party_is_missing(self):
+		acc_name = "Additional Debtors"
+		if not frappe.db.get_value(
+			"Account", filters={"account_name": acc_name, "company": self.company}
+		):
+			additional_receivable_acc = frappe.get_doc(
+				{
+					"doctype": "Account",
+					"account_name": acc_name,
+					"parent_account": "Accounts Receivable - " + self.company_abbr,
+					"company": self.company,
+					"account_type": "Receivable",
+				}
+			).save()
+			self.debtors2 = additional_receivable_acc.name
+
+		je = frappe.new_doc("Journal Entry")
+		je.company = self.company
+		je.posting_date = today()
+		je.append(
+			"accounts",
+			{
+				"account": self.debit_to,
+				"party_type": "Customer",
+				"party": self.customer,
+				"debit_in_account_currency": 150,
+				"credit_in_account_currency": 0,
+				"cost_center": self.cost_center,
+			},
+		)
+		je.append(
+			"accounts",
+			{
+				"account": self.debtors2,
+				"party_type": "Customer",
+				"party": self.customer,
+				"debit_in_account_currency": 200,
+				"credit_in_account_currency": 0,
+				"cost_center": self.cost_center,
+			},
+		)
+		je.append(
+			"accounts",
+			{
+				"account": self.cash,
+				"debit_in_account_currency": 0,
+				"credit_in_account_currency": 350,
+				"cost_center": self.cost_center,
+			},
+		)
+		je.save().submit()
+
+		# manually remove party from Payment Ledger
+		ple = qb.DocType("Payment Ledger Entry")
+		qb.update(ple).set(ple.party, None).where(ple.voucher_no == je.name).run()
+
+		filters = {
+			"company": self.company,
+			"report_date": today(),
+			"range1": 30,
+			"range2": 60,
+			"range3": 90,
+			"range4": 120,
+		}
+
+		report_ouput = execute(filters)[1]
+		expected_data = [
+			[self.debtors2, je.doctype, je.name, "Customer", self.customer, 200.0, 0.0, 0.0, 200.0],
+			[self.debit_to, je.doctype, je.name, "Customer", self.customer, 150.0, 0.0, 0.0, 150.0],
+		]
+		self.assertEqual(len(report_ouput), 2)
+		# fetch only required fields
+		report_output = [
+			[
+				x.party_account,
+				x.voucher_type,
+				x.voucher_no,
+				"Customer",
+				self.customer,
+				x.invoiced,
+				x.paid,
+				x.credit_note,
+				x.outstanding,
+			]
+			for x in report_ouput
+		]
+		# use account name to sort
+		# post sorting output should be [[Additional Debtors, ...], [Debtors, ...]]
+		report_output = sorted(report_output, key=lambda x: x[0])
+		self.assertEqual(expected_data, report_output)
diff --git a/erpnext/accounts/report/financial_ratios/financial_ratios.py b/erpnext/accounts/report/financial_ratios/financial_ratios.py
index 57421eb..47b4fd0 100644
--- a/erpnext/accounts/report/financial_ratios/financial_ratios.py
+++ b/erpnext/accounts/report/financial_ratios/financial_ratios.py
@@ -177,8 +177,8 @@
 	return_on_equity_ratio = {"ratio": "Return on Equity Ratio"}
 
 	for year in years:
-		profit_after_tax = total_income[year] + total_expense[year]
-		share_holder_fund = total_asset[year] - total_liability[year]
+		profit_after_tax = flt(total_income.get(year)) + flt(total_expense.get(year))
+		share_holder_fund = flt(total_asset.get(year)) - flt(total_liability.get(year))
 
 		debt_equity_ratio[year] = calculate_ratio(
 			total_liability.get(year), share_holder_fund, precision
diff --git a/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py b/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py
index 553c137..099884a 100644
--- a/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py
+++ b/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py
@@ -133,15 +133,17 @@
 			self.gle_balances = set(val.gle) | self.gle_balances
 			self.ple_balances = set(val.ple) | self.ple_balances
 
-		self.diff1 = self.gle_balances.difference(self.ple_balances)
-		self.diff2 = self.ple_balances.difference(self.gle_balances)
+		self.variation_in_payment_ledger = self.gle_balances.difference(self.ple_balances)
+		self.variation_in_general_ledger = self.ple_balances.difference(self.gle_balances)
 		self.diff = frappe._dict({})
 
-		for x in self.diff1:
+		for x in self.variation_in_payment_ledger:
 			self.diff[(x[0], x[1], x[2], x[3])] = frappe._dict({"gl_balance": x[4]})
 
-		for x in self.diff2:
-			self.diff[(x[0], x[1], x[2], x[3])].update(frappe._dict({"pl_balance": x[4]}))
+		for x in self.variation_in_general_ledger:
+			self.diff.setdefault((x[0], x[1], x[2], x[3]), frappe._dict({"gl_balance": 0.0})).update(
+				frappe._dict({"pl_balance": x[4]})
+			)
 
 	def generate_data(self):
 		self.data = []
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index 3324a73..38060bb 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -544,6 +544,8 @@
 						new_row.qty += flt(row.qty)
 						new_row.buying_amount += flt(row.buying_amount, self.currency_precision)
 						new_row.base_amount += flt(row.base_amount, self.currency_precision)
+						if self.filters.get("group_by") == "Sales Person":
+							new_row.allocated_amount += flt(row.allocated_amount, self.currency_precision)
 				new_row = self.set_average_rate(new_row)
 				self.grouped_data.append(new_row)
 
diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js
index 4a3d9bb..b6bbd97 100644
--- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js
+++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js
@@ -32,13 +32,6 @@
 			"label": __("Accounting Dimension"),
 			"fieldtype": "Link",
 			"options": "Accounting Dimension",
-			"get_query": () =>{
-				return {
-					filters: {
-						"disabled": 0
-					}
-				}
-			}
 		},
 		{
 			"fieldname": "fiscal_year",
diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
index 91ad3d6..eac5426 100644
--- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
+++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
@@ -68,7 +68,11 @@
 				tax_amount += entry.credit - entry.debit
 
 			if net_total_map.get(name):
-				total_amount, grand_total, base_total = net_total_map.get(name)
+				if voucher_type == "Journal Entry" and tax_amount and rate:
+					# back calcalute total amount from rate and tax_amount
+					total_amount = grand_total = base_total = tax_amount / (rate / 100)
+				else:
+					total_amount, grand_total, base_total = net_total_map.get(name)
 			else:
 				total_amount += entry.credit
 
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 555ed4f..1c7052f 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -645,7 +645,7 @@
 		"outstanding_amount": d.outstanding_amount,
 		"allocated_amount": d.allocated_amount,
 		"exchange_rate": d.exchange_rate if d.exchange_gain_loss else payment_entry.get_exchange_rate(),
-		"exchange_gain_loss": d.exchange_gain_loss,  # only populated from invoice in case of advance allocation
+		"exchange_gain_loss": d.exchange_gain_loss,
 		"account": d.account,
 	}
 
@@ -658,28 +658,29 @@
 				existing_row.reference_doctype, existing_row.reference_name
 			).set_total_advance_paid()
 
-		original_row = existing_row.as_dict().copy()
-		existing_row.update(reference_details)
+		if d.allocated_amount <= existing_row.allocated_amount:
+			existing_row.allocated_amount -= d.allocated_amount
 
-		if d.allocated_amount < original_row.allocated_amount:
 			new_row = payment_entry.append("references")
 			new_row.docstatus = 1
 			for field in list(reference_details):
-				new_row.set(field, original_row[field])
+				new_row.set(field, reference_details[field])
 
-			new_row.allocated_amount = original_row.allocated_amount - d.allocated_amount
 	else:
 		new_row = payment_entry.append("references")
 		new_row.docstatus = 1
 		new_row.update(reference_details)
 
 	payment_entry.flags.ignore_validate_update_after_submit = True
+	payment_entry.clear_unallocated_reference_document_rows()
 	payment_entry.setup_party_account_field()
 	payment_entry.set_missing_values()
 	if not skip_ref_details_update_for_pe:
 		payment_entry.set_missing_ref_details()
 	payment_entry.set_amounts()
-	payment_entry.make_exchange_gain_loss_journal()
+	payment_entry.make_exchange_gain_loss_journal(
+		frappe._dict({"difference_posting_date": d.difference_posting_date})
+	)
 
 	if not do_not_save:
 		payment_entry.save(ignore_permissions=True)
diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js
index 5395f15..d378fbd 100644
--- a/erpnext/assets/doctype/asset/asset.js
+++ b/erpnext/assets/doctype/asset/asset.js
@@ -9,7 +9,6 @@
 		frm.set_query("item_code", function() {
 			return {
 				"filters": {
-					"disabled": 0,
 					"is_fixed_asset": 1,
 					"is_stock_item": 0
 				}
@@ -337,7 +336,7 @@
 
 
 	item_code: function(frm) {
-		if(frm.doc.item_code && frm.doc.calculate_depreciation) {
+		if(frm.doc.item_code && frm.doc.calculate_depreciation && frm.doc.gross_purchase_amount) {
 			frm.trigger('set_finance_book');
 		} else {
 			frm.set_value('finance_books', []);
@@ -490,7 +489,7 @@
 
 	calculate_depreciation: function(frm) {
 		frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation);
-		if (frm.doc.item_code && frm.doc.calculate_depreciation ) {
+		if (frm.doc.item_code && frm.doc.calculate_depreciation && frm.doc.gross_purchase_amount) {
 			frm.trigger("set_finance_book");
 		} else {
 			frm.set_value("finance_books", []);
diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json
index c7d08e2..40f51ab 100644
--- a/erpnext/assets/doctype/asset/asset.json
+++ b/erpnext/assets/doctype/asset/asset.json
@@ -221,11 +221,11 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:!(doc.is_composite_asset && !doc.capitalized_in)",
    "fieldname": "gross_purchase_amount",
    "fieldtype": "Currency",
    "label": "Gross Purchase Amount",
    "options": "Company:company:default_currency",
-   "read_only": 1,
    "read_only_depends_on": "eval:!doc.is_existing_asset",
    "reqd": 1
   },
@@ -399,6 +399,7 @@
    "fieldtype": "Column Break"
   },
   {
+   "depends_on": "eval:!doc.is_composite_asset && !doc.is_existing_asset",
    "fieldname": "purchase_receipt",
    "fieldtype": "Link",
    "label": "Purchase Receipt",
@@ -416,6 +417,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:!doc.is_composite_asset && !doc.is_existing_asset",
    "fieldname": "purchase_invoice",
    "fieldtype": "Link",
    "label": "Purchase Invoice",
@@ -479,10 +481,11 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval.doc.asset_quantity",
    "fieldname": "asset_quantity",
    "fieldtype": "Int",
    "label": "Asset Quantity",
-   "read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset"
+   "read_only": 1
   },
   {
    "fieldname": "depr_entry_posting_status",
@@ -562,9 +565,14 @@
    "link_doctype": "Journal Entry",
    "link_fieldname": "reference_name",
    "table_fieldname": "accounts"
+  },
+  {
+   "group": "Asset Capitalization",
+   "link_doctype": "Asset Capitalization",
+   "link_fieldname": "target_asset"
   }
  ],
- "modified": "2023-10-03 23:28:26.732269",
+ "modified": "2023-10-27 17:03:46.629617",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Asset",
@@ -608,4 +616,4 @@
  "states": [],
  "title_field": "asset_name",
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index 88ef69c..d69f5ef 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -19,7 +19,6 @@
 from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
 from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
 from erpnext.assets.doctype.asset.asset import (
-	get_asset_value_after_depreciation,
 	make_sales_invoice,
 	split_asset,
 	update_maintenance_status,
@@ -194,6 +193,7 @@
 	def test_is_fixed_asset_set(self):
 		asset = create_asset(is_existing_asset=1)
 		doc = frappe.new_doc("Purchase Invoice")
+		doc.company = "_Test Company"
 		doc.supplier = "_Test Supplier"
 		doc.append("items", {"item_code": "Macbook Pro", "qty": 1, "asset": asset.name})
 
@@ -534,7 +534,7 @@
 
 		self.assertEqual("Asset Received But Not Billed - _TC", doc.items[0].expense_account)
 
-	# CWIP: Capital Work In Progress
+	# Capital Work In Progress
 	def test_cwip_accounting(self):
 		pr = make_purchase_receipt(
 			item_code="Macbook Pro", qty=1, rate=5000, do_not_submit=True, location="Test Location"
@@ -567,7 +567,8 @@
 		pr.submit()
 
 		expected_gle = (
-			("Asset Received But Not Billed - _TC", 0.0, 5250.0),
+			("_Test Account Shipping Charges - _TC", 0.0, 250.0),
+			("Asset Received But Not Billed - _TC", 0.0, 5000.0),
 			("CWIP Account - _TC", 5250.0, 0.0),
 		)
 
@@ -586,9 +587,8 @@
 		expected_gle = (
 			("_Test Account Service Tax - _TC", 250.0, 0.0),
 			("_Test Account Shipping Charges - _TC", 250.0, 0.0),
-			("Asset Received But Not Billed - _TC", 5250.0, 0.0),
+			("Asset Received But Not Billed - _TC", 5000.0, 0.0),
 			("Creditors - _TC", 0.0, 5500.0),
-			("Expenses Included In Asset Valuation - _TC", 0.0, 250.0),
 		)
 
 		pi_gle = frappe.db.sql(
@@ -1789,6 +1789,7 @@
 			"fixed_asset_account": "_Test Fixed Asset - _TC",
 			"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
 			"depreciation_expense_account": "_Test Depreciations - _TC",
+			"capital_work_in_progress_account": "CWIP Account - _TC",
 		},
 	)
 	asset_category.append(
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
index 0d6f6b4..728764b 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
@@ -876,12 +876,8 @@
 		"amount",
 	]
 
-	pi_items = frappe.get_all(
-		"Purchase Invoice Item", filters={"wip_composite_asset": asset}, fields=fields
-	)
-
 	pr_items = frappe.get_all(
 		"Purchase Receipt Item", filters={"wip_composite_asset": asset}, fields=fields
 	)
 
-	return pi_items + pr_items
+	return pr_items
diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json
index 8c73e56..0599992 100644
--- a/erpnext/buying/doctype/buying_settings/buying_settings.json
+++ b/erpnext/buying/doctype/buying_settings/buying_settings.json
@@ -16,7 +16,7 @@
   "transaction_settings_section",
   "po_required",
   "pr_required",
-  "over_order_allowance",
+  "blanket_order_allowance",
   "column_break_12",
   "maintain_same_rate",
   "set_landed_cost_based_on_purchase_invoice_rate",
@@ -24,6 +24,7 @@
   "bill_for_rejected_quantity_in_purchase_invoice",
   "disable_last_purchase_rate",
   "show_pay_button",
+  "use_transaction_date_exchange_rate",
   "subcontract",
   "backflush_raw_materials_of_subcontract_based_on",
   "column_break_11",
@@ -160,10 +161,17 @@
   },
   {
    "default": "0",
-   "description": "Percentage you are allowed to order more against the Blanket Order Quantity. For example: If you have a Blanket Order of Quantity 100 units. and your Allowance is 10% then you are allowed to order 110 units.",
-   "fieldname": "over_order_allowance",
+   "description": "While making Purchase Invoice from Purchase Order, use Exchange Rate on Invoice's transaction date rather than inheriting it from Purchase Order. Only applies for Purchase Invoice.",
+   "fieldname": "use_transaction_date_exchange_rate",
+   "fieldtype": "Check",
+   "label": "Use Transaction Date Exchange Rate"
+  },
+  {
+   "default": "0",
+   "description": "Percentage you are allowed to order beyond the Blanket Order quantity.",
+   "fieldname": "blanket_order_allowance",
    "fieldtype": "Float",
-   "label": "Over Order Allowance (%)"
+   "label": "Blanket Order Allowance (%)"
   }
  ],
  "icon": "fa fa-cog",
@@ -171,7 +179,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2023-03-02 17:02:14.404622",
+ "modified": "2023-10-25 14:03:32.520418",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Buying Settings",
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index ca60348..2d7dbd6 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -559,6 +559,9 @@
 					"bom": "bom",
 					"material_request": "material_request",
 					"material_request_item": "material_request_item",
+					"sales_order": "sales_order",
+					"sales_order_item": "sales_order_item",
+					"wip_composite_asset": "wip_composite_asset",
 				},
 				"postprocess": update_item,
 				"condition": lambda doc: abs(doc.received_qty) < abs(doc.qty)
@@ -635,6 +638,7 @@
 			"field_map": {
 				"name": "po_detail",
 				"parent": "purchase_order",
+				"wip_composite_asset": "wip_composite_asset",
 			},
 			"postprocess": update_item,
 			"condition": lambda doc: (doc.base_amount == 0 or abs(doc.billed_amt) < abs(doc.amount)),
diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
index 6b29984..b1da97d 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -86,6 +86,8 @@
   "billed_amt",
   "accounting_details",
   "expense_account",
+  "column_break_fyqr",
+  "wip_composite_asset",
   "manufacture_details",
   "manufacturer",
   "manufacturer_part_no",
@@ -896,13 +898,23 @@
    "fieldname": "apply_tds",
    "fieldtype": "Check",
    "label": "Apply TDS"
+  },
+  {
+   "fieldname": "wip_composite_asset",
+   "fieldtype": "Link",
+   "label": "WIP Composite Asset",
+   "options": "Asset"
+  },
+  {
+   "fieldname": "column_break_fyqr",
+   "fieldtype": "Column Break"
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-09-13 16:22:40.825092",
+ "modified": "2023-10-27 15:50:42.655573",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order Item",
@@ -915,4 +927,4 @@
  "sort_order": "DESC",
  "states": [],
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier/supplier_dashboard.py b/erpnext/buying/doctype/supplier/supplier_dashboard.py
index 11bb06e..3bd306e 100644
--- a/erpnext/buying/doctype/supplier/supplier_dashboard.py
+++ b/erpnext/buying/doctype/supplier/supplier_dashboard.py
@@ -8,7 +8,7 @@
 			"This is based on transactions against this Supplier. See timeline below for details"
 		),
 		"fieldname": "supplier",
-		"non_standard_fieldnames": {"Payment Entry": "party_name", "Bank Account": "party"},
+		"non_standard_fieldnames": {"Payment Entry": "party", "Bank Account": "party"},
 		"transactions": [
 			{"label": _("Procurement"), "items": ["Request for Quotation", "Supplier Quotation"]},
 			{"label": _("Orders"), "items": ["Purchase Order", "Purchase Receipt", "Purchase Invoice"]},
diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
index 6e22acf..683a12a 100644
--- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
+++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
@@ -334,6 +334,11 @@
 			"variable_label": "Total Ordered",
 			"path": "get_ordered_qty",
 		},
+		{
+			"param_name": "total_invoiced",
+			"variable_label": "Total Invoiced",
+			"path": "get_invoiced_qty",
+		},
 	]
 	install_standing_docs = [
 		{
diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py
index 4080d1f..6c91a04 100644
--- a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py
+++ b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py
@@ -440,6 +440,23 @@
 	).run(as_list=True)[0][0] or 0
 
 
+def get_invoiced_qty(scorecard):
+	"""Returns the total number of invoiced quantity (based on Purchase Invoice)"""
+
+	pi = frappe.qb.DocType("Purchase Invoice")
+
+	return (
+		frappe.qb.from_(pi)
+		.select(Sum(pi.total_qty))
+		.where(
+			(pi.supplier == scorecard.supplier)
+			& (pi.docstatus == 1)
+			& (pi.posting_date >= scorecard.get("start_date"))
+			& (pi.posting_date <= scorecard.get("end_date"))
+		)
+	).run(as_list=True)[0][0] or 0
+
+
 def get_rfq_total_number(scorecard):
 	"""Gets the total number of RFQs sent to supplier"""
 	supplier = frappe.get_doc("Supplier", scorecard.supplier)
diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
index e10c0e2..b6e4630 100644
--- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
+++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
@@ -6,7 +6,7 @@
 
 import frappe
 from frappe import _
-from frappe.query_builder.functions import IfNull
+from frappe.query_builder.functions import IfNull, Sum
 from frappe.utils import date_diff, flt, getdate
 
 
@@ -57,7 +57,7 @@
 			po_item.qty,
 			po_item.received_qty,
 			(po_item.qty - po_item.received_qty).as_("pending_qty"),
-			IfNull(pi_item.qty, 0).as_("billed_qty"),
+			Sum(IfNull(pi_item.qty, 0)).as_("billed_qty"),
 			po_item.base_amount.as_("amount"),
 			(po_item.received_qty * po_item.base_rate).as_("received_qty_amount"),
 			(po_item.billed_amt * IfNull(po.conversion_rate, 1)).as_("billed_amount"),
diff --git a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js
index fd73b87..579c0a6 100644
--- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js
+++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js
@@ -44,11 +44,6 @@
 						}
 					}
 				}
-				else {
-					return {
-						filters: { "disabled": 0 }
-					}
-				}
 			}
 		},
 		{
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 42dd847..6ea154c 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -13,6 +13,7 @@
 	add_days,
 	add_months,
 	cint,
+	comma_and,
 	flt,
 	fmt_money,
 	formatdate,
@@ -181,6 +182,17 @@
 		self.validate_party_account_currency()
 
 		if self.doctype in ["Purchase Invoice", "Sales Invoice"]:
+			if invalid_advances := [
+				x for x in self.advances if not x.reference_type or not x.reference_name
+			]:
+				frappe.throw(
+					_(
+						"Rows: {0} in {1} section are Invalid. Reference Name should point to a valid Payment Entry or Journal Entry."
+					).format(
+						frappe.bold(comma_and([x.idx for x in invalid_advances])), frappe.bold(_("Advance Payments"))
+					)
+				)
+
 			pos_check_field = "is_pos" if self.doctype == "Sales Invoice" else "is_paid"
 			if cint(self.allocate_advances_automatically) and not cint(self.get(pos_check_field)):
 				self.set_advances()
@@ -572,6 +584,17 @@
 					self.currency, self.company_currency, transaction_date, args
 				)
 
+			if (
+				self.currency
+				and buying_or_selling == "Buying"
+				and frappe.db.get_single_value("Buying Settings", "use_transaction_date_exchange_rate")
+				and self.doctype == "Purchase Invoice"
+			):
+				self.use_transaction_date_exchange_rate = True
+				self.conversion_rate = get_exchange_rate(
+					self.currency, self.company_currency, transaction_date, args
+				)
+
 	def set_missing_item_details(self, for_validate=False):
 		"""set missing item values"""
 		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@@ -1155,7 +1178,9 @@
 								self.name,
 								arg.get("referenced_row"),
 							):
-								posting_date = frappe.db.get_value(arg.voucher_type, arg.voucher_no, "posting_date")
+								posting_date = arg.get("difference_posting_date") or frappe.db.get_value(
+									arg.voucher_type, arg.voucher_no, "posting_date"
+								)
 								je = create_gain_loss_journal(
 									self.company,
 									posting_date,
@@ -1238,7 +1263,7 @@
 
 						je = create_gain_loss_journal(
 							self.company,
-							self.posting_date,
+							args.get("difference_posting_date") if args else self.posting_date,
 							self.party_type,
 							self.party,
 							party_account,
diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py
index e68ee90..c8785a5 100644
--- a/erpnext/controllers/item_variant.py
+++ b/erpnext/controllers/item_variant.py
@@ -9,6 +9,8 @@
 from frappe import _
 from frappe.utils import cstr, flt
 
+from erpnext.utilities.product import get_item_codes_by_attributes
+
 
 class ItemVariantExistsError(frappe.ValidationError):
 	pass
@@ -24,7 +26,8 @@
 
 @frappe.whitelist()
 def get_variant(template, args=None, variant=None, manufacturer=None, manufacturer_part_no=None):
-	"""Validates Attributes and their Values, then looks for an exactly
+	"""
+	Validates Attributes and their Values, then looks for an exactly
 	matching Item Variant
 
 	:param item: Template Item
@@ -34,13 +37,14 @@
 
 	if item_template.variant_based_on == "Manufacturer" and manufacturer:
 		return make_variant_based_on_manufacturer(item_template, manufacturer, manufacturer_part_no)
-	else:
-		if isinstance(args, str):
-			args = json.loads(args)
 
-		if not args:
-			frappe.throw(_("Please specify at least one attribute in the Attributes table"))
-		return find_variant(template, args, variant)
+	if isinstance(args, str):
+		args = json.loads(args)
+
+	if not args:
+		frappe.throw(_("Please specify at least one attribute in the Attributes table"))
+
+	return find_variant(template, args, variant)
 
 
 def make_variant_based_on_manufacturer(template, manufacturer, manufacturer_part_no):
@@ -157,17 +161,6 @@
 
 
 def find_variant(template, args, variant_item_code=None):
-	conditions = [
-		"""(iv_attribute.attribute={0} and iv_attribute.attribute_value={1})""".format(
-			frappe.db.escape(key), frappe.db.escape(cstr(value))
-		)
-		for key, value in args.items()
-	]
-
-	conditions = " or ".join(conditions)
-
-	from erpnext.e_commerce.variant_selector.utils import get_item_codes_by_attributes
-
 	possible_variants = [
 		i for i in get_item_codes_by_attributes(args, template) if i != variant_item_code
 	]
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 418a56f..d34fbeb 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -4,9 +4,9 @@
 
 import frappe
 from frappe import _, bold, throw
-from frappe.contacts.doctype.address.address import get_address_display
 from frappe.utils import cint, flt, get_link_to_form, nowtime
 
+from erpnext.accounts.party import render_address
 from erpnext.controllers.accounts_controller import get_taxes_and_charges
 from erpnext.controllers.sales_and_purchase_return import get_rate_for_return
 from erpnext.controllers.stock_controller import StockController
@@ -602,7 +602,9 @@
 
 		for address_field, address_display_field in address_dict.items():
 			if self.get(address_field):
-				self.set(address_display_field, get_address_display(self.get(address_field)))
+				self.set(
+					address_display_field, render_address(self.get(address_field), check_permissions=False)
+				)
 
 	def validate_for_duplicate_items(self):
 		check_list, chk_dupl_itm = [], []
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index ae54b80..a7330ec 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -62,9 +62,12 @@
 			)
 		)
 
+		is_asset_pr = any(d.get("is_fixed_asset") for d in self.get("items"))
+
 		if (
 			cint(erpnext.is_perpetual_inventory_enabled(self.company))
 			or provisional_accounting_for_non_stock_items
+			or is_asset_pr
 		):
 			warehouse_account = get_warehouse_account_map(self.company)
 
@@ -73,11 +76,6 @@
 					gl_entries = self.get_gl_entries(warehouse_account)
 				make_gl_entries(gl_entries, from_repost=from_repost)
 
-		elif self.doctype in ["Purchase Receipt", "Purchase Invoice"] and self.docstatus == 1:
-			gl_entries = []
-			gl_entries = self.get_asset_gl_entry(gl_entries)
-			make_gl_entries(gl_entries, from_repost=from_repost)
-
 	def validate_serialized_batch(self):
 		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 
@@ -692,13 +690,21 @@
 				d.stock_uom_rate = d.rate / (d.conversion_factor or 1)
 
 	def validate_internal_transfer(self):
-		if (
-			self.doctype in ("Sales Invoice", "Delivery Note", "Purchase Invoice", "Purchase Receipt")
-			and self.is_internal_transfer()
-		):
-			self.validate_in_transit_warehouses()
-			self.validate_multi_currency()
-			self.validate_packed_items()
+		if self.doctype in ("Sales Invoice", "Delivery Note", "Purchase Invoice", "Purchase Receipt"):
+			if self.is_internal_transfer():
+				self.validate_in_transit_warehouses()
+				self.validate_multi_currency()
+				self.validate_packed_items()
+			else:
+				self.validate_internal_transfer_warehouse()
+
+	def validate_internal_transfer_warehouse(self):
+		for row in self.items:
+			if row.get("target_warehouse"):
+				row.target_warehouse = None
+
+			if row.get("from_warehouse"):
+				row.from_warehouse = None
 
 	def validate_in_transit_warehouses(self):
 		if (
@@ -855,8 +861,9 @@
 
 @frappe.whitelist()
 def show_accounting_ledger_preview(company, doctype, docname):
-	filters = {"company": company, "include_dimensions": 1}
+	filters = frappe._dict(company=company, include_dimensions=1)
 	doc = frappe.get_doc(doctype, docname)
+	doc.run_method("before_gl_preview")
 
 	gl_columns, gl_data = get_accounting_ledger_preview(doc, filters)
 
@@ -867,8 +874,9 @@
 
 @frappe.whitelist()
 def show_stock_ledger_preview(company, doctype, docname):
-	filters = {"company": company}
+	filters = frappe._dict(company=company)
 	doc = frappe.get_doc(doctype, docname)
+	doc.run_method("before_sl_preview")
 
 	sl_columns, sl_data = get_stock_ledger_preview(doc, filters)
 
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 95bf0e4..96284d6 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -25,6 +25,9 @@
 	def __init__(self, doc: Document):
 		self.doc = doc
 		frappe.flags.round_off_applicable_accounts = []
+		frappe.flags.round_row_wise_tax = frappe.db.get_single_value(
+			"Accounts Settings", "round_row_wise_tax"
+		)
 
 		self._items = self.filter_rows() if self.doc.doctype == "Quotation" else self.doc.get("items")
 
@@ -370,6 +373,8 @@
 			for i, tax in enumerate(self.doc.get("taxes")):
 				# tax_amount represents the amount of tax for the current step
 				current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
+				if frappe.flags.round_row_wise_tax:
+					current_tax_amount = flt(current_tax_amount, tax.precision("tax_amount"))
 
 				# Adjust divisional loss to the last item
 				if tax.charge_type == "Actual":
@@ -480,10 +485,19 @@
 		# store tax breakup for each item
 		key = item.item_code or item.item_name
 		item_wise_tax_amount = current_tax_amount * self.doc.conversion_rate
-		if tax.item_wise_tax_detail.get(key):
-			item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
+		if frappe.flags.round_row_wise_tax:
+			item_wise_tax_amount = flt(item_wise_tax_amount, tax.precision("tax_amount"))
+			if tax.item_wise_tax_detail.get(key):
+				item_wise_tax_amount += flt(tax.item_wise_tax_detail[key][1], tax.precision("tax_amount"))
+			tax.item_wise_tax_detail[key] = [
+				tax_rate,
+				flt(item_wise_tax_amount, tax.precision("tax_amount")),
+			]
+		else:
+			if tax.item_wise_tax_detail.get(key):
+				item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
 
-		tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount)]
+			tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount)]
 
 	def round_off_totals(self, tax):
 		if tax.account_head in frappe.flags.round_off_applicable_accounts:
diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py
index 391258f..97d3c5c 100644
--- a/erpnext/controllers/tests/test_accounts_controller.py
+++ b/erpnext/controllers/tests/test_accounts_controller.py
@@ -7,7 +7,7 @@
 from frappe import qb
 from frappe.query_builder.functions import Sum
 from frappe.tests.utils import FrappeTestCase, change_settings
-from frappe.utils import add_days, flt, nowdate
+from frappe.utils import add_days, flt, getdate, nowdate
 
 from erpnext import get_default_cost_center
 from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
@@ -614,6 +614,73 @@
 		self.assertEqual(exc_je_for_si, [])
 		self.assertEqual(exc_je_for_pe, [])
 
+	def test_15_gain_loss_on_different_posting_date(self):
+		# Invoice in Foreign Currency
+		si = self.create_sales_invoice(
+			posting_date=add_days(nowdate(), -2), qty=2, conversion_rate=80, rate=1
+		)
+		# Payment
+		pe = (
+			self.create_payment_entry(posting_date=add_days(nowdate(), -1), amount=2, source_exc_rate=75)
+			.save()
+			.submit()
+		)
+
+		# There should be outstanding in both currencies
+		si.reload()
+		self.assertEqual(si.outstanding_amount, 2)
+		self.assert_ledger_outstanding(si.doctype, si.name, 160.0, 2.0)
+
+		# Reconcile the remaining amount
+		pr = frappe.get_doc("Payment Reconciliation")
+		pr.company = self.company
+		pr.party_type = "Customer"
+		pr.party = self.customer
+		pr.receivable_payable_account = self.debit_usd
+		pr.get_unreconciled_entries()
+		self.assertEqual(len(pr.invoices), 1)
+		self.assertEqual(len(pr.payments), 1)
+		invoices = [x.as_dict() for x in pr.invoices]
+		payments = [x.as_dict() for x in pr.payments]
+		pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+		pr.allocation[0].gain_loss_posting_date = add_days(nowdate(), 1)
+		pr.reconcile()
+
+		# Exchange Gain/Loss Journal should've been created.
+		exc_je_for_si = self.get_journals_for(si.doctype, si.name)
+		exc_je_for_pe = self.get_journals_for(pe.doctype, pe.name)
+		self.assertNotEqual(exc_je_for_si, [])
+		self.assertEqual(len(exc_je_for_si), 1)
+		self.assertEqual(len(exc_je_for_pe), 1)
+		self.assertEqual(exc_je_for_si[0], exc_je_for_pe[0])
+
+		self.assertEqual(
+			frappe.db.get_value("Journal Entry", exc_je_for_si[0].parent, "posting_date"),
+			getdate(add_days(nowdate(), 1)),
+		)
+
+		self.assertEqual(len(pr.invoices), 0)
+		self.assertEqual(len(pr.payments), 0)
+
+		# There should be no outstanding
+		si.reload()
+		self.assertEqual(si.outstanding_amount, 0)
+		self.assert_ledger_outstanding(si.doctype, si.name, 0.0, 0.0)
+
+		# Cancel Payment
+		pe.reload()
+		pe.cancel()
+
+		si.reload()
+		self.assertEqual(si.outstanding_amount, 2)
+		self.assert_ledger_outstanding(si.doctype, si.name, 160.0, 2.0)
+
+		# Exchange Gain/Loss Journal should've been cancelled
+		exc_je_for_si = self.get_journals_for(si.doctype, si.name)
+		exc_je_for_pe = self.get_journals_for(pe.doctype, pe.name)
+		self.assertEqual(exc_je_for_si, [])
+		self.assertEqual(exc_je_for_pe, [])
+
 	def test_20_journal_against_sales_invoice(self):
 		# Invoice in Foreign Currency
 		si = self.create_sales_invoice(qty=1, conversion_rate=80, rate=1)
diff --git a/erpnext/e_commerce/__init__.py b/erpnext/e_commerce/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/e_commerce/__init__.py
+++ /dev/null
diff --git a/erpnext/e_commerce/api.py b/erpnext/e_commerce/api.py
deleted file mode 100644
index bfada0f..0000000
--- a/erpnext/e_commerce/api.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-import json
-
-import frappe
-from frappe.utils import cint
-
-from erpnext.e_commerce.product_data_engine.filters import ProductFiltersBuilder
-from erpnext.e_commerce.product_data_engine.query import ProductQuery
-from erpnext.setup.doctype.item_group.item_group import get_child_groups_for_website
-
-
-@frappe.whitelist(allow_guest=True)
-def get_product_filter_data(query_args=None):
-	"""
-	Returns filtered products and discount filters.
-	:param query_args (dict): contains filters to get products list
-
-	Query Args filters:
-	search (str): Search Term.
-	field_filters (dict): Keys include item_group, brand, etc.
-	attribute_filters(dict): Keys include Color, Size, etc.
-	start (int): Offset items by
-	item_group (str): Valid Item Group
-	from_filters (bool): Set as True to jump to page 1
-	"""
-	if isinstance(query_args, str):
-		query_args = json.loads(query_args)
-
-	query_args = frappe._dict(query_args)
-	if query_args:
-		search = query_args.get("search")
-		field_filters = query_args.get("field_filters", {})
-		attribute_filters = query_args.get("attribute_filters", {})
-		start = cint(query_args.start) if query_args.get("start") else 0
-		item_group = query_args.get("item_group")
-		from_filters = query_args.get("from_filters")
-	else:
-		search, attribute_filters, item_group, from_filters = None, None, None, None
-		field_filters = {}
-		start = 0
-
-	# if new filter is checked, reset start to show filtered items from page 1
-	if from_filters:
-		start = 0
-
-	sub_categories = []
-	if item_group:
-		sub_categories = get_child_groups_for_website(item_group, immediate=True)
-
-	engine = ProductQuery()
-	try:
-		result = engine.query(
-			attribute_filters, field_filters, search_term=search, start=start, item_group=item_group
-		)
-	except Exception:
-		frappe.log_error("Product query with filter failed")
-		return {"exc": "Something went wrong!"}
-
-	# discount filter data
-	filters = {}
-	discounts = result["discounts"]
-
-	if discounts:
-		filter_engine = ProductFiltersBuilder()
-		filters["discount_filters"] = filter_engine.get_discount_filters(discounts)
-
-	return {
-		"items": result["items"] or [],
-		"filters": filters,
-		"settings": engine.settings,
-		"sub_categories": sub_categories,
-		"items_count": result["items_count"],
-	}
-
-
-@frappe.whitelist(allow_guest=True)
-def get_guest_redirect_on_action():
-	return frappe.db.get_single_value("E Commerce Settings", "redirect_on_action")
diff --git a/erpnext/e_commerce/doctype/__init__.py b/erpnext/e_commerce/doctype/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/e_commerce/doctype/__init__.py
+++ /dev/null
diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/__init__.py b/erpnext/e_commerce/doctype/e_commerce_settings/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/e_commerce/doctype/e_commerce_settings/__init__.py
+++ /dev/null
diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js
deleted file mode 100644
index c37fa2f..0000000
--- a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on("E Commerce Settings", {
-	onload: function(frm) {
-		if(frm.doc.__onload && frm.doc.__onload.quotation_series) {
-			frm.fields_dict.quotation_series.df.options = frm.doc.__onload.quotation_series;
-			frm.refresh_field("quotation_series");
-		}
-
-		frm.set_query('payment_gateway_account', function() {
-			return { 'filters': { 'payment_channel': "Email" } };
-		});
-	},
-	refresh: function(frm) {
-		if (frm.doc.enabled) {
-			frm.get_field('store_page_docs').$wrapper.removeClass('hide-control').html(
-				`<div>${__("Follow these steps to create a landing page for your store")}:
-					<a href="https://docs.erpnext.com/docs/user/manual/en/website/store-landing-page"
-						style="color: var(--gray-600)">
-						docs/store-landing-page
-					</a>
-				</div>`
-			);
-		}
-
-		frappe.model.with_doctype("Website Item", () => {
-			const web_item_meta = frappe.get_meta('Website Item');
-
-			const valid_fields = web_item_meta.fields.filter(df =>
-				["Link", "Table MultiSelect"].includes(df.fieldtype) && !df.hidden
-			).map(df =>
-				({ label: df.label, value: df.fieldname })
-			);
-
-			frm.get_field("filter_fields").grid.update_docfield_property(
-				'fieldname', 'options', valid_fields
-			);
-		});
-	},
-	enabled: function(frm) {
-		if (frm.doc.enabled === 1) {
-			frm.set_value('enable_variants', 1);
-		}
-		else {
-			frm.set_value('company', '');
-			frm.set_value('price_list', '');
-			frm.set_value('default_customer_group', '');
-			frm.set_value('quotation_series', '');
-		}
-	},
-
-	enable_checkout: function(frm) {
-		if (frm.doc.enable_checkout) {
-			erpnext.utils.check_payments_app();
-		}
-	}
-});
diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json
deleted file mode 100644
index e6f08f7..0000000
--- a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json
+++ /dev/null
@@ -1,395 +0,0 @@
-{
- "actions": [],
- "creation": "2021-02-10 17:13:39.139103",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "products_per_page",
-  "filter_categories_section",
-  "enable_field_filters",
-  "filter_fields",
-  "enable_attribute_filters",
-  "filter_attributes",
-  "display_settings_section",
-  "hide_variants",
-  "enable_variants",
-  "show_price",
-  "column_break_9",
-  "show_stock_availability",
-  "show_quantity_in_website",
-  "allow_items_not_in_stock",
-  "column_break_13",
-  "show_apply_coupon_code_in_website",
-  "show_contact_us_button",
-  "show_attachments",
-  "section_break_18",
-  "company",
-  "price_list",
-  "enabled",
-  "store_page_docs",
-  "column_break_21",
-  "default_customer_group",
-  "quotation_series",
-  "checkout_settings_section",
-  "enable_checkout",
-  "show_price_in_quotation",
-  "column_break_27",
-  "save_quotations_as_draft",
-  "payment_gateway_account",
-  "payment_success_url",
-  "add_ons_section",
-  "enable_wishlist",
-  "column_break_22",
-  "enable_reviews",
-  "column_break_23",
-  "enable_recommendations",
-  "item_search_settings_section",
-  "redisearch_warning",
-  "search_index_fields",
-  "is_redisearch_enabled",
-  "is_redisearch_loaded",
-  "shop_by_category_section",
-  "slideshow",
-  "guest_display_settings_section",
-  "hide_price_for_guest",
-  "redirect_on_action"
- ],
- "fields": [
-  {
-   "default": "6",
-   "fieldname": "products_per_page",
-   "fieldtype": "Int",
-   "label": "Products per Page"
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "filter_categories_section",
-   "fieldtype": "Section Break",
-   "label": "Filters and Categories"
-  },
-  {
-   "default": "0",
-   "fieldname": "hide_variants",
-   "fieldtype": "Check",
-   "label": "Hide Variants"
-  },
-  {
-   "default": "0",
-   "description": "The field filters will also work as categories in the <b>Shop by Category</b> page.",
-   "fieldname": "enable_field_filters",
-   "fieldtype": "Check",
-   "label": "Enable Field Filters (Categories)"
-  },
-  {
-   "default": "0",
-   "fieldname": "enable_attribute_filters",
-   "fieldtype": "Check",
-   "label": "Enable Attribute Filters"
-  },
-  {
-   "depends_on": "enable_field_filters",
-   "fieldname": "filter_fields",
-   "fieldtype": "Table",
-   "label": "Website Item Fields",
-   "options": "Website Filter Field"
-  },
-  {
-   "depends_on": "enable_attribute_filters",
-   "fieldname": "filter_attributes",
-   "fieldtype": "Table",
-   "label": "Attributes",
-   "options": "Website Attribute"
-  },
-  {
-   "default": "0",
-   "fieldname": "enabled",
-   "fieldtype": "Check",
-   "in_list_view": 1,
-   "label": "Enable Shopping Cart"
-  },
-  {
-   "depends_on": "doc.enabled",
-   "fieldname": "store_page_docs",
-   "fieldtype": "HTML"
-  },
-  {
-   "fieldname": "display_settings_section",
-   "fieldtype": "Section Break",
-   "label": "Display Settings"
-  },
-  {
-   "default": "0",
-   "fieldname": "show_attachments",
-   "fieldtype": "Check",
-   "label": "Show Public Attachments"
-  },
-  {
-   "default": "0",
-   "fieldname": "show_price",
-   "fieldtype": "Check",
-   "label": "Show Price"
-  },
-  {
-   "default": "0",
-   "fieldname": "show_stock_availability",
-   "fieldtype": "Check",
-   "label": "Show Stock Availability"
-  },
-  {
-   "default": "0",
-   "fieldname": "enable_variants",
-   "fieldtype": "Check",
-   "label": "Enable Variant Selection"
-  },
-  {
-   "fieldname": "column_break_13",
-   "fieldtype": "Column Break"
-  },
-  {
-   "default": "0",
-   "fieldname": "show_contact_us_button",
-   "fieldtype": "Check",
-   "label": "Show Contact Us Button"
-  },
-  {
-   "default": "0",
-   "depends_on": "show_stock_availability",
-   "fieldname": "show_quantity_in_website",
-   "fieldtype": "Check",
-   "label": "Show Stock Quantity"
-  },
-  {
-   "default": "0",
-   "fieldname": "show_apply_coupon_code_in_website",
-   "fieldtype": "Check",
-   "label": "Show Apply Coupon Code"
-  },
-  {
-   "default": "0",
-   "fieldname": "allow_items_not_in_stock",
-   "fieldtype": "Check",
-   "label": "Allow items not in stock to be added to cart"
-  },
-  {
-   "fieldname": "section_break_18",
-   "fieldtype": "Section Break",
-   "label": "Shopping Cart"
-  },
-  {
-   "depends_on": "enabled",
-   "fieldname": "company",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Company",
-   "mandatory_depends_on": "eval: doc.enabled === 1",
-   "options": "Company",
-   "remember_last_selected_value": 1
-  },
-  {
-   "depends_on": "enabled",
-   "description": "Prices will not be shown if Price List is not set",
-   "fieldname": "price_list",
-   "fieldtype": "Link",
-   "label": "Price List",
-   "mandatory_depends_on": "eval: doc.enabled === 1",
-   "options": "Price List"
-  },
-  {
-   "fieldname": "column_break_21",
-   "fieldtype": "Column Break"
-  },
-  {
-   "depends_on": "enabled",
-   "fieldname": "default_customer_group",
-   "fieldtype": "Link",
-   "ignore_user_permissions": 1,
-   "label": "Default Customer Group",
-   "mandatory_depends_on": "eval: doc.enabled === 1",
-   "options": "Customer Group"
-  },
-  {
-   "depends_on": "enabled",
-   "fieldname": "quotation_series",
-   "fieldtype": "Select",
-   "label": "Quotation Series",
-   "mandatory_depends_on": "eval: doc.enabled === 1"
-  },
-  {
-   "collapsible": 1,
-   "collapsible_depends_on": "eval:doc.enable_checkout",
-   "depends_on": "enabled",
-   "fieldname": "checkout_settings_section",
-   "fieldtype": "Section Break",
-   "label": "Checkout Settings"
-  },
-  {
-   "default": "0",
-   "fieldname": "enable_checkout",
-   "fieldtype": "Check",
-   "label": "Enable Checkout"
-  },
-  {
-   "default": "Orders",
-   "depends_on": "enable_checkout",
-   "description": "After payment completion redirect user to selected page.",
-   "fieldname": "payment_success_url",
-   "fieldtype": "Select",
-   "label": "Payment Success Url",
-   "mandatory_depends_on": "enable_checkout",
-   "options": "\nOrders\nInvoices\nMy Account"
-  },
-  {
-   "fieldname": "column_break_27",
-   "fieldtype": "Column Break"
-  },
-  {
-   "default": "0",
-   "depends_on": "eval: doc.enable_checkout == 0",
-   "fieldname": "save_quotations_as_draft",
-   "fieldtype": "Check",
-   "label": "Save Quotations as Draft"
-  },
-  {
-   "depends_on": "enable_checkout",
-   "fieldname": "payment_gateway_account",
-   "fieldtype": "Link",
-   "label": "Payment Gateway Account",
-   "mandatory_depends_on": "enable_checkout",
-   "options": "Payment Gateway Account"
-  },
-  {
-   "collapsible": 1,
-   "depends_on": "enable_field_filters",
-   "fieldname": "shop_by_category_section",
-   "fieldtype": "Section Break",
-   "label": "Shop by Category"
-  },
-  {
-   "fieldname": "slideshow",
-   "fieldtype": "Link",
-   "label": "Slideshow",
-   "options": "Website Slideshow"
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "add_ons_section",
-   "fieldtype": "Section Break",
-   "label": "Add-ons"
-  },
-  {
-   "default": "0",
-   "fieldname": "enable_wishlist",
-   "fieldtype": "Check",
-   "label": "Enable Wishlist"
-  },
-  {
-   "default": "0",
-   "fieldname": "enable_reviews",
-   "fieldtype": "Check",
-   "label": "Enable Reviews and Ratings"
-  },
-  {
-   "fieldname": "search_index_fields",
-   "fieldtype": "Small Text",
-   "label": "Search Index Fields",
-   "mandatory_depends_on": "is_redisearch_enabled",
-   "read_only_depends_on": "eval:!doc.is_redisearch_loaded"
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "item_search_settings_section",
-   "fieldtype": "Section Break",
-   "label": "Item Search Settings"
-  },
-  {
-   "default": "0",
-   "fieldname": "is_redisearch_loaded",
-   "fieldtype": "Check",
-   "hidden": 1,
-   "label": "Is Redisearch Loaded"
-  },
-  {
-   "depends_on": "eval:!doc.is_redisearch_loaded",
-   "fieldname": "redisearch_warning",
-   "fieldtype": "HTML",
-   "label": "Redisearch Warning",
-   "options": "<p class=\"alert alert-warning\">Redisearch is not loaded. If you want to use the advanced product search feature, refer <a class=\"alert-link\" href=\"https://docs.erpnext.com/docs/v13/user/manual/en/setting-up/articles/installing-redisearch\" target=\"_blank\">here</a>.</p>"
-  },
-  {
-   "default": "0",
-   "depends_on": "eval:doc.show_price",
-   "fieldname": "hide_price_for_guest",
-   "fieldtype": "Check",
-   "label": "Hide Price for Guest"
-  },
-  {
-   "fieldname": "column_break_9",
-   "fieldtype": "Column Break"
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "guest_display_settings_section",
-   "fieldtype": "Section Break",
-   "label": "Guest Display Settings"
-  },
-  {
-   "description": "Link to redirect Guest on actions that need login such as add to cart, wishlist, etc. <b>E.g.: /login</b>",
-   "fieldname": "redirect_on_action",
-   "fieldtype": "Data",
-   "label": "Redirect on Action"
-  },
-  {
-   "fieldname": "column_break_22",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "column_break_23",
-   "fieldtype": "Column Break"
-  },
-  {
-   "default": "0",
-   "fieldname": "enable_recommendations",
-   "fieldtype": "Check",
-   "label": "Enable Recommendations"
-  },
-  {
-   "default": "0",
-   "depends_on": "eval: doc.enable_checkout == 0",
-   "fieldname": "show_price_in_quotation",
-   "fieldtype": "Check",
-   "label": "Show Price in Quotation"
-  },
-  {
-   "default": "0",
-   "fieldname": "is_redisearch_enabled",
-   "fieldtype": "Check",
-   "label": "Enable Redisearch",
-   "read_only_depends_on": "eval:!doc.is_redisearch_loaded"
-  }
- ],
- "index_web_pages_for_search": 1,
- "issingle": 1,
- "links": [],
- "modified": "2022-04-01 18:35:56.106756",
- "modified_by": "Administrator",
- "module": "E-commerce",
- "name": "E Commerce Settings",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "print": 1,
-   "read": 1,
-   "role": "System Manager",
-   "share": 1,
-   "write": 1
-  }
- ],
- "sort_field": "modified",
- "sort_order": "DESC",
- "states": [],
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py
deleted file mode 100644
index c27d29a..0000000
--- a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.py
+++ /dev/null
@@ -1,185 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-import frappe
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import comma_and, flt, unique
-
-from erpnext.e_commerce.redisearch_utils import (
-	create_website_items_index,
-	define_autocomplete_dictionary,
-	get_indexable_web_fields,
-	is_search_module_loaded,
-)
-
-
-class ShoppingCartSetupError(frappe.ValidationError):
-	pass
-
-
-class ECommerceSettings(Document):
-	def onload(self):
-		self.get("__onload").quotation_series = frappe.get_meta("Quotation").get_options("naming_series")
-
-		# flag >> if redisearch is installed and loaded
-		self.is_redisearch_loaded = is_search_module_loaded()
-
-	def validate(self):
-		self.validate_field_filters(self.filter_fields, self.enable_field_filters)
-		self.validate_attribute_filters()
-		self.validate_checkout()
-		self.validate_search_index_fields()
-
-		if self.enabled:
-			self.validate_price_list_exchange_rate()
-
-		frappe.clear_document_cache("E Commerce Settings", "E Commerce Settings")
-
-		self.is_redisearch_enabled_pre_save = frappe.db.get_single_value(
-			"E Commerce Settings", "is_redisearch_enabled"
-		)
-
-	def after_save(self):
-		self.create_redisearch_indexes()
-
-	def create_redisearch_indexes(self):
-		# if redisearch is enabled (value changed) create indexes and dictionary
-		value_changed = self.is_redisearch_enabled != self.is_redisearch_enabled_pre_save
-		if self.is_redisearch_loaded and self.is_redisearch_enabled and value_changed:
-			define_autocomplete_dictionary()
-			create_website_items_index()
-
-	@staticmethod
-	def validate_field_filters(filter_fields, enable_field_filters):
-		if not (enable_field_filters and filter_fields):
-			return
-
-		web_item_meta = frappe.get_meta("Website Item")
-		valid_fields = [
-			df.fieldname for df in web_item_meta.fields if df.fieldtype in ["Link", "Table MultiSelect"]
-		]
-
-		for row in filter_fields:
-			if row.fieldname not in valid_fields:
-				frappe.throw(
-					_(
-						"Filter Fields Row #{0}: Fieldname {1} must be of type 'Link' or 'Table MultiSelect'"
-					).format(row.idx, frappe.bold(row.fieldname))
-				)
-
-	def validate_attribute_filters(self):
-		if not (self.enable_attribute_filters and self.filter_attributes):
-			return
-
-		# if attribute filters are enabled, hide_variants should be disabled
-		self.hide_variants = 0
-
-	def validate_checkout(self):
-		if self.enable_checkout and not self.payment_gateway_account:
-			self.enable_checkout = 0
-
-	def validate_search_index_fields(self):
-		if not self.search_index_fields:
-			return
-
-		fields = self.search_index_fields.replace(" ", "")
-		fields = unique(fields.strip(",").split(","))  # Remove extra ',' and remove duplicates
-
-		# All fields should be indexable
-		allowed_indexable_fields = get_indexable_web_fields()
-
-		if not (set(fields).issubset(allowed_indexable_fields)):
-			invalid_fields = list(set(fields).difference(allowed_indexable_fields))
-			num_invalid_fields = len(invalid_fields)
-			invalid_fields = comma_and(invalid_fields)
-
-			if num_invalid_fields > 1:
-				frappe.throw(
-					_("{0} are not valid options for Search Index Field.").format(frappe.bold(invalid_fields))
-				)
-			else:
-				frappe.throw(
-					_("{0} is not a valid option for Search Index Field.").format(frappe.bold(invalid_fields))
-				)
-
-		self.search_index_fields = ",".join(fields)
-
-	def validate_price_list_exchange_rate(self):
-		"Check if exchange rate exists for Price List currency (to Company's currency)."
-		from erpnext.setup.utils import get_exchange_rate
-
-		if not self.enabled or not self.company or not self.price_list:
-			return  # this function is also called from hooks, check values again
-
-		company_currency = frappe.get_cached_value("Company", self.company, "default_currency")
-		price_list_currency = frappe.db.get_value("Price List", self.price_list, "currency")
-
-		if not company_currency:
-			msg = f"Please specify currency in Company {self.company}"
-			frappe.throw(_(msg), title=_("Missing Currency"), exc=ShoppingCartSetupError)
-
-		if not price_list_currency:
-			msg = f"Please specify currency in Price List {frappe.bold(self.price_list)}"
-			frappe.throw(_(msg), title=_("Missing Currency"), exc=ShoppingCartSetupError)
-
-		if price_list_currency != company_currency:
-			from_currency, to_currency = price_list_currency, company_currency
-
-			# Get exchange rate checks Currency Exchange Records too
-			exchange_rate = get_exchange_rate(from_currency, to_currency, args="for_selling")
-
-			if not flt(exchange_rate):
-				msg = f"Missing Currency Exchange Rates for {from_currency}-{to_currency}"
-				frappe.throw(_(msg), title=_("Missing"), exc=ShoppingCartSetupError)
-
-	def validate_tax_rule(self):
-		if not frappe.db.get_value("Tax Rule", {"use_for_shopping_cart": 1}, "name"):
-			frappe.throw(frappe._("Set Tax Rule for shopping cart"), ShoppingCartSetupError)
-
-	def get_tax_master(self, billing_territory):
-		tax_master = self.get_name_from_territory(
-			billing_territory, "sales_taxes_and_charges_masters", "sales_taxes_and_charges_master"
-		)
-		return tax_master and tax_master[0] or None
-
-	def get_shipping_rules(self, shipping_territory):
-		return self.get_name_from_territory(shipping_territory, "shipping_rules", "shipping_rule")
-
-	def on_change(self):
-		old_doc = self.get_doc_before_save()
-
-		if old_doc:
-			old_fields = old_doc.search_index_fields
-			new_fields = self.search_index_fields
-
-			# if search index fields get changed
-			if not (new_fields == old_fields):
-				create_website_items_index()
-
-
-def validate_cart_settings(doc=None, method=None):
-	frappe.get_doc("E Commerce Settings", "E Commerce Settings").run_method("validate")
-
-
-def get_shopping_cart_settings():
-	return frappe.get_cached_doc("E Commerce Settings")
-
-
-@frappe.whitelist(allow_guest=True)
-def is_cart_enabled():
-	return get_shopping_cart_settings().enabled
-
-
-def show_quantity_in_website():
-	return get_shopping_cart_settings().show_quantity_in_website
-
-
-def check_shopping_cart_enabled():
-	if not get_shopping_cart_settings().enabled:
-		frappe.throw(_("You need to enable Shopping Cart"), ShoppingCartSetupError)
-
-
-def show_attachments():
-	return get_shopping_cart_settings().show_attachments
diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/test_e_commerce_settings.py b/erpnext/e_commerce/doctype/e_commerce_settings/test_e_commerce_settings.py
deleted file mode 100644
index 662db4d..0000000
--- a/erpnext/e_commerce/doctype/e_commerce_settings/test_e_commerce_settings.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-import unittest
-
-import frappe
-
-from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import (
-	ShoppingCartSetupError,
-)
-
-
-class TestECommerceSettings(unittest.TestCase):
-	def tearDown(self):
-		frappe.db.rollback()
-
-	def test_tax_rule_validation(self):
-		frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 0")
-		frappe.db.commit()  # nosemgrep
-
-		cart_settings = frappe.get_doc("E Commerce Settings")
-		cart_settings.enabled = 1
-		if not frappe.db.get_value("Tax Rule", {"use_for_shopping_cart": 1}, "name"):
-			self.assertRaises(ShoppingCartSetupError, cart_settings.validate_tax_rule)
-
-		frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 1")
-
-	def test_invalid_filter_fields(self):
-		"Check if Item fields are blocked in E Commerce Settings filter fields."
-		from frappe.custom.doctype.custom_field.custom_field import create_custom_field
-
-		setup_e_commerce_settings({"enable_field_filters": 1})
-
-		create_custom_field(
-			"Item",
-			dict(owner="Administrator", fieldname="test_data", label="Test", fieldtype="Data"),
-		)
-		settings = frappe.get_doc("E Commerce Settings")
-		settings.append("filter_fields", {"fieldname": "test_data"})
-
-		self.assertRaises(frappe.ValidationError, settings.save)
-
-
-def setup_e_commerce_settings(values_dict):
-	"Accepts a dict of values that updates E Commerce Settings."
-	if not values_dict:
-		return
-
-	doc = frappe.get_doc("E Commerce Settings", "E Commerce Settings")
-	doc.update(values_dict)
-	doc.save()
-
-
-test_dependencies = ["Tax Rule"]
diff --git a/erpnext/e_commerce/doctype/item_review/__init__.py b/erpnext/e_commerce/doctype/item_review/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/e_commerce/doctype/item_review/__init__.py
+++ /dev/null
diff --git a/erpnext/e_commerce/doctype/item_review/item_review.js b/erpnext/e_commerce/doctype/item_review/item_review.js
deleted file mode 100644
index a57c370..0000000
--- a/erpnext/e_commerce/doctype/item_review/item_review.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Item Review', {
-	// refresh: function(frm) {
-
-	// }
-});
diff --git a/erpnext/e_commerce/doctype/item_review/item_review.json b/erpnext/e_commerce/doctype/item_review/item_review.json
deleted file mode 100644
index 57f719f..0000000
--- a/erpnext/e_commerce/doctype/item_review/item_review.json
+++ /dev/null
@@ -1,134 +0,0 @@
-{
- "actions": [],
- "beta": 1,
- "creation": "2021-03-23 16:47:26.542226",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "website_item",
-  "user",
-  "customer",
-  "column_break_3",
-  "item",
-  "published_on",
-  "reviews_section",
-  "review_title",
-  "rating",
-  "comment"
- ],
- "fields": [
-  {
-   "fieldname": "website_item",
-   "fieldtype": "Link",
-   "label": "Website Item",
-   "options": "Website Item",
-   "read_only": 1,
-   "reqd": 1
-  },
-  {
-   "fieldname": "user",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "User",
-   "options": "User",
-   "read_only": 1
-  },
-  {
-   "fieldname": "column_break_3",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fetch_from": "website_item.item_code",
-   "fieldname": "item",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Item",
-   "options": "Item",
-   "read_only": 1
-  },
-  {
-   "fieldname": "reviews_section",
-   "fieldtype": "Section Break",
-   "label": "Reviews"
-  },
-  {
-   "fieldname": "rating",
-   "fieldtype": "Rating",
-   "in_list_view": 1,
-   "label": "Rating",
-   "read_only": 1
-  },
-  {
-   "fieldname": "comment",
-   "fieldtype": "Small Text",
-   "label": "Comment",
-   "read_only": 1
-  },
-  {
-   "fieldname": "review_title",
-   "fieldtype": "Data",
-   "label": "Review Title",
-   "read_only": 1
-  },
-  {
-   "fieldname": "customer",
-   "fieldtype": "Link",
-   "label": "Customer",
-   "options": "Customer",
-   "read_only": 1
-  },
-  {
-   "fieldname": "published_on",
-   "fieldtype": "Data",
-   "label": "Published on",
-   "read_only": 1
-  }
- ],
- "index_web_pages_for_search": 1,
- "links": [],
- "modified": "2021-08-10 12:08:58.119691",
- "modified_by": "Administrator",
- "module": "E-commerce",
- "name": "Item Review",
- "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": "Website Manager",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "report": 1,
-   "role": "Customer",
-   "share": 1
-  }
- ],
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/e_commerce/doctype/item_review/item_review.py b/erpnext/e_commerce/doctype/item_review/item_review.py
deleted file mode 100644
index 3e540e3..0000000
--- a/erpnext/e_commerce/doctype/item_review/item_review.py
+++ /dev/null
@@ -1,153 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from datetime import datetime
-
-import frappe
-from frappe import _
-from frappe.contacts.doctype.contact.contact import get_contact_name
-from frappe.model.document import Document
-from frappe.utils import cint, flt
-
-from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import (
-	get_shopping_cart_settings,
-)
-
-
-class UnverifiedReviewer(frappe.ValidationError):
-	pass
-
-
-class ItemReview(Document):
-	def after_insert(self):
-		# regenerate cache on review creation
-		reviews_dict = get_queried_reviews(self.website_item)
-		set_reviews_in_cache(self.website_item, reviews_dict)
-
-	def after_delete(self):
-		# regenerate cache on review deletion
-		reviews_dict = get_queried_reviews(self.website_item)
-		set_reviews_in_cache(self.website_item, reviews_dict)
-
-
-@frappe.whitelist()
-def get_item_reviews(web_item, start=0, end=10, data=None):
-	"Get Website Item Review Data."
-	start, end = cint(start), cint(end)
-	settings = get_shopping_cart_settings()
-
-	# Get cached reviews for first page (start=0)
-	# avoid cache when page is different
-	from_cache = not bool(start)
-
-	if not data:
-		data = frappe._dict()
-
-	if settings and settings.get("enable_reviews"):
-		reviews_cache = frappe.cache().hget("item_reviews", web_item)
-		if from_cache and reviews_cache:
-			data = reviews_cache
-		else:
-			data = get_queried_reviews(web_item, start, end, data)
-			if from_cache:
-				set_reviews_in_cache(web_item, data)
-
-	return data
-
-
-def get_queried_reviews(web_item, start=0, end=10, data=None):
-	"""
-	Query Website Item wise reviews and cache if needed.
-	Cache stores only first page of reviews i.e. 10 reviews maximum.
-	Returns:
-	        dict: Containing reviews, average ratings, % of reviews per rating and total reviews.
-	"""
-	if not data:
-		data = frappe._dict()
-
-	data.reviews = frappe.db.get_all(
-		"Item Review",
-		filters={"website_item": web_item},
-		fields=["*"],
-		limit_start=start,
-		limit_page_length=end,
-	)
-
-	rating_data = frappe.db.get_all(
-		"Item Review",
-		filters={"website_item": web_item},
-		fields=["avg(rating) as average, count(*) as total"],
-	)[0]
-
-	data.average_rating = flt(rating_data.average, 1)
-	data.average_whole_rating = flt(data.average_rating, 0)
-
-	# get % of reviews per rating
-	reviews_per_rating = []
-	for i in range(1, 6):
-		count = frappe.db.get_all(
-			"Item Review", filters={"website_item": web_item, "rating": i}, fields=["count(*) as count"]
-		)[0].count
-
-		percent = flt((count / rating_data.total or 1) * 100, 0) if count else 0
-		reviews_per_rating.append(percent)
-
-	data.reviews_per_rating = reviews_per_rating
-	data.total_reviews = rating_data.total
-
-	return data
-
-
-def set_reviews_in_cache(web_item, reviews_dict):
-	frappe.cache().hset("item_reviews", web_item, reviews_dict)
-
-
-@frappe.whitelist()
-def add_item_review(web_item, title, rating, comment=None):
-	"""Add an Item Review by a user if non-existent."""
-	if frappe.session.user == "Guest":
-		# guest user should not reach here ideally in the case they do via an API, throw error
-		frappe.throw(_("You are not verified to write a review yet."), exc=UnverifiedReviewer)
-
-	if not frappe.db.exists("Item Review", {"user": frappe.session.user, "website_item": web_item}):
-		doc = frappe.get_doc(
-			{
-				"doctype": "Item Review",
-				"user": frappe.session.user,
-				"customer": get_customer(),
-				"website_item": web_item,
-				"item": frappe.db.get_value("Website Item", web_item, "item_code"),
-				"review_title": title,
-				"rating": rating,
-				"comment": comment,
-			}
-		)
-		doc.published_on = datetime.today().strftime("%d %B %Y")
-		doc.insert()
-
-
-def get_customer(silent=False):
-	"""
-	silent: Return customer if exists else return nothing. Dont throw error.
-	"""
-	user = frappe.session.user
-	contact_name = get_contact_name(user)
-	customer = None
-
-	if contact_name:
-		contact = frappe.get_doc("Contact", contact_name)
-		for link in contact.links:
-			if link.link_doctype == "Customer":
-				customer = link.link_name
-				break
-
-	if customer:
-		return frappe.db.get_value("Customer", customer)
-	elif silent:
-		return None
-	else:
-		# should not reach here unless via an API
-		frappe.throw(
-			_("You are not a verified customer yet. Please contact us to proceed."), exc=UnverifiedReviewer
-		)
diff --git a/erpnext/e_commerce/doctype/item_review/test_item_review.py b/erpnext/e_commerce/doctype/item_review/test_item_review.py
deleted file mode 100644
index 8a4befc..0000000
--- a/erpnext/e_commerce/doctype/item_review/test_item_review.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-import unittest
-
-import frappe
-from frappe.core.doctype.user_permission.test_user_permission import create_user
-
-from erpnext.e_commerce.doctype.e_commerce_settings.test_e_commerce_settings import (
-	setup_e_commerce_settings,
-)
-from erpnext.e_commerce.doctype.item_review.item_review import (
-	UnverifiedReviewer,
-	add_item_review,
-	get_item_reviews,
-)
-from erpnext.e_commerce.doctype.website_item.website_item import make_website_item
-from erpnext.e_commerce.shopping_cart.cart import get_party
-from erpnext.stock.doctype.item.test_item import make_item
-
-
-class TestItemReview(unittest.TestCase):
-	def setUp(self):
-		item = make_item("Test Mobile Phone")
-		if not frappe.db.exists("Website Item", {"item_code": "Test Mobile Phone"}):
-			make_website_item(item, save=True)
-
-		setup_e_commerce_settings({"enable_reviews": 1})
-		frappe.local.shopping_cart_settings = None
-
-	def tearDown(self):
-		frappe.get_cached_doc("Website Item", {"item_code": "Test Mobile Phone"}).delete()
-		setup_e_commerce_settings({"enable_reviews": 0})
-
-	def test_add_and_get_item_reviews_from_customer(self):
-		"Add / Get Reviews from a User that is a valid customer (has added to cart or purchased in the past)"
-		# create user
-		web_item = frappe.db.get_value("Website Item", {"item_code": "Test Mobile Phone"})
-		test_user = create_user("test_reviewer@example.com", "Customer")
-		frappe.set_user(test_user.name)
-
-		# create customer and contact against user
-		customer = get_party()
-
-		# post review on "Test Mobile Phone"
-		try:
-			add_item_review(web_item, "Great Product", 3, "Would recommend this product")
-			review_name = frappe.db.get_value("Item Review", {"website_item": web_item})
-		except Exception:
-			self.fail(f"Error while publishing review for {web_item}")
-
-		review_data = get_item_reviews(web_item, 0, 10)
-
-		self.assertEqual(len(review_data.reviews), 1)
-		self.assertEqual(review_data.average_rating, 3)
-		self.assertEqual(review_data.reviews_per_rating[2], 100)
-
-		# tear down
-		frappe.set_user("Administrator")
-		frappe.delete_doc("Item Review", review_name)
-		customer.delete()
-
-	def test_add_item_review_from_non_customer(self):
-		"Check if logged in user (who is not a customer yet) is blocked from posting reviews."
-		web_item = frappe.db.get_value("Website Item", {"item_code": "Test Mobile Phone"})
-		test_user = create_user("test_reviewer@example.com", "Customer")
-		frappe.set_user(test_user.name)
-
-		with self.assertRaises(UnverifiedReviewer):
-			add_item_review(web_item, "Great Product", 3, "Would recommend this product")
-
-		# tear down
-		frappe.set_user("Administrator")
-
-	def test_add_item_reviews_from_guest_user(self):
-		"Check if Guest user is blocked from posting reviews."
-		web_item = frappe.db.get_value("Website Item", {"item_code": "Test Mobile Phone"})
-		frappe.set_user("Guest")
-
-		with self.assertRaises(UnverifiedReviewer):
-			add_item_review(web_item, "Great Product", 3, "Would recommend this product")
-
-		# tear down
-		frappe.set_user("Administrator")
diff --git a/erpnext/e_commerce/doctype/recommended_items/__init__.py b/erpnext/e_commerce/doctype/recommended_items/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/e_commerce/doctype/recommended_items/__init__.py
+++ /dev/null
diff --git a/erpnext/e_commerce/doctype/recommended_items/recommended_items.json b/erpnext/e_commerce/doctype/recommended_items/recommended_items.json
deleted file mode 100644
index 1821532..0000000
--- a/erpnext/e_commerce/doctype/recommended_items/recommended_items.json
+++ /dev/null
@@ -1,88 +0,0 @@
-{
- "actions": [],
- "creation": "2021-07-12 20:52:12.503470",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "website_item",
-  "website_item_name",
-  "column_break_2",
-  "item_code",
-  "more_information_section",
-  "route",
-  "column_break_6",
-  "website_item_image",
-  "website_item_thumbnail"
- ],
- "fields": [
-  {
-   "fieldname": "website_item",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Website Item",
-   "options": "Website Item"
-  },
-  {
-   "fetch_from": "website_item.web_item_name",
-   "fieldname": "website_item_name",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Website Item Name",
-   "read_only": 1
-  },
-  {
-   "fieldname": "column_break_2",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "more_information_section",
-   "fieldtype": "Section Break",
-   "label": "More Information"
-  },
-  {
-   "fetch_from": "website_item.route",
-   "fieldname": "route",
-   "fieldtype": "Small Text",
-   "label": "Route",
-   "read_only": 1
-  },
-  {
-   "fetch_from": "website_item.website_image",
-   "fieldname": "website_item_image",
-   "fieldtype": "Attach",
-   "label": "Website Item Image",
-   "read_only": 1
-  },
-  {
-   "fieldname": "column_break_6",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fetch_from": "website_item.thumbnail",
-   "fieldname": "website_item_thumbnail",
-   "fieldtype": "Data",
-   "label": "Website Item Thumbnail",
-   "read_only": 1
-  },
-  {
-   "fetch_from": "website_item.item_code",
-   "fieldname": "item_code",
-   "fieldtype": "Data",
-   "label": "Item Code"
-  }
- ],
- "index_web_pages_for_search": 1,
- "istable": 1,
- "links": [],
- "modified": "2022-06-28 16:44:24.718728",
- "modified_by": "Administrator",
- "module": "E-commerce",
- "name": "Recommended Items",
- "owner": "Administrator",
- "permissions": [],
- "sort_field": "modified",
- "sort_order": "DESC",
- "states": [],
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/e_commerce/doctype/recommended_items/recommended_items.py b/erpnext/e_commerce/doctype/recommended_items/recommended_items.py
deleted file mode 100644
index 16b6e52..0000000
--- a/erpnext/e_commerce/doctype/recommended_items/recommended_items.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-# import frappe
-from frappe.model.document import Document
-
-
-class RecommendedItems(Document):
-	pass
diff --git a/erpnext/e_commerce/doctype/website_item/__init__.py b/erpnext/e_commerce/doctype/website_item/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/e_commerce/doctype/website_item/__init__.py
+++ /dev/null
diff --git a/erpnext/e_commerce/doctype/website_item/templates/website_item.html b/erpnext/e_commerce/doctype/website_item/templates/website_item.html
deleted file mode 100644
index db12309..0000000
--- a/erpnext/e_commerce/doctype/website_item/templates/website_item.html
+++ /dev/null
@@ -1,7 +0,0 @@
-{% extends "templates/web.html" %}
-
-{% block page_content %}
-<h1>{{ title }}</h1>
-{% endblock %}
-
-<!-- this is a sample default web page template -->
\ No newline at end of file
diff --git a/erpnext/e_commerce/doctype/website_item/templates/website_item_row.html b/erpnext/e_commerce/doctype/website_item/templates/website_item_row.html
deleted file mode 100644
index d7014b4..0000000
--- a/erpnext/e_commerce/doctype/website_item/templates/website_item_row.html
+++ /dev/null
@@ -1,4 +0,0 @@
-<div>
-	<a href="{{ doc.route }}">{{ doc.title or doc.name }}</a>
-</div>
-<!-- this is a sample default list template -->
diff --git a/erpnext/e_commerce/doctype/website_item/test_website_item.py b/erpnext/e_commerce/doctype/website_item/test_website_item.py
deleted file mode 100644
index 2ba84c0..0000000
--- a/erpnext/e_commerce/doctype/website_item/test_website_item.py
+++ /dev/null
@@ -1,564 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-
-import frappe
-
-from erpnext.controllers.item_variant import create_variant
-from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import (
-	get_shopping_cart_settings,
-)
-from erpnext.e_commerce.doctype.e_commerce_settings.test_e_commerce_settings import (
-	setup_e_commerce_settings,
-)
-from erpnext.e_commerce.doctype.website_item.website_item import make_website_item
-from erpnext.e_commerce.shopping_cart.product_info import get_product_info_for_website
-from erpnext.stock.doctype.item.item import DataValidationError
-from erpnext.stock.doctype.item.test_item import make_item
-
-WEBITEM_DESK_TESTS = ("test_website_item_desk_item_sync", "test_publish_variant_and_template")
-WEBITEM_PRICE_TESTS = (
-	"test_website_item_price_for_logged_in_user",
-	"test_website_item_price_for_guest_user",
-)
-
-
-class TestWebsiteItem(unittest.TestCase):
-	@classmethod
-	def setUpClass(cls):
-		setup_e_commerce_settings(
-			{
-				"company": "_Test Company",
-				"enabled": 1,
-				"default_customer_group": "_Test Customer Group",
-				"price_list": "_Test Price List India",
-			}
-		)
-
-	@classmethod
-	def tearDownClass(cls):
-		frappe.db.rollback()
-
-	def setUp(self):
-		if self._testMethodName in WEBITEM_DESK_TESTS:
-			make_item(
-				"Test Web Item",
-				{
-					"has_variant": 1,
-					"variant_based_on": "Item Attribute",
-					"attributes": [{"attribute": "Test Size"}],
-				},
-			)
-		elif self._testMethodName in WEBITEM_PRICE_TESTS:
-			create_user_and_customer_if_not_exists(
-				"test_contact_customer@example.com", "_Test Contact For _Test Customer"
-			)
-			create_regular_web_item()
-			make_web_item_price(item_code="Test Mobile Phone")
-
-			# Note: When testing web item pricing rule logged-in user pricing rule must differ from guest pricing rule or test will falsely pass.
-			# 	  This is because make_web_pricing_rule creates a pricing rule "selling": 1, without specifying "applicable_for". Therefor,
-			# 	  when testing for logged-in user the test will get the previous pricing rule because "selling" is still true.
-			#
-			#     I've attempted to mitigate this by setting applicable_for=Customer, and customer=Guest however, this only results in PermissionError failing the test.
-			make_web_pricing_rule(
-				title="Test Pricing Rule for Test Mobile Phone", item_code="Test Mobile Phone", selling=1
-			)
-			make_web_pricing_rule(
-				title="Test Pricing Rule for Test Mobile Phone (Customer)",
-				item_code="Test Mobile Phone",
-				selling=1,
-				discount_percentage="25",
-				applicable_for="Customer",
-				customer="_Test Customer",
-			)
-
-	def test_index_creation(self):
-		"Check if index is getting created in db."
-		from erpnext.e_commerce.doctype.website_item.website_item import on_doctype_update
-
-		on_doctype_update()
-
-		indices = frappe.db.sql("show index from `tabWebsite Item`", as_dict=1)
-		expected_columns = {"route", "item_group", "brand"}
-		for index in indices:
-			expected_columns.discard(index.get("Column_name"))
-
-		if expected_columns:
-			self.fail(f"Expected db index on these columns: {', '.join(expected_columns)}")
-
-	def test_website_item_desk_item_sync(self):
-		"Check creation/updation/deletion of Website Item and its impact on Item master."
-		web_item = None
-		item = make_item("Test Web Item")  # will return item if exists
-		try:
-			web_item = make_website_item(item, save=False)
-			web_item.save()
-		except Exception:
-			self.fail(f"Error while creating website item for {item}")
-
-		# check if website item was created
-		self.assertTrue(bool(web_item))
-		self.assertTrue(bool(web_item.route))
-
-		item.reload()
-		self.assertEqual(web_item.published, 1)
-		self.assertEqual(item.published_in_website, 1)  # check if item was back updated
-		self.assertEqual(web_item.item_group, item.item_group)
-
-		# check if changing item data changes it in website item
-		item.item_name = "Test Web Item 1"
-		item.stock_uom = "Unit"
-		item.save()
-		web_item.reload()
-		self.assertEqual(web_item.item_name, item.item_name)
-		self.assertEqual(web_item.stock_uom, item.stock_uom)
-
-		# check if disabling item unpublished website item
-		item.disabled = 1
-		item.save()
-		web_item.reload()
-		self.assertEqual(web_item.published, 0)
-
-		# check if website item deletion, unpublishes desk item
-		web_item.delete()
-		item.reload()
-		self.assertEqual(item.published_in_website, 0)
-
-		item.delete()
-
-	def test_publish_variant_and_template(self):
-		"Check if template is published on publishing variant."
-		# template "Test Web Item" created on setUp
-		variant = create_variant("Test Web Item", {"Test Size": "Large"})
-		variant.save()
-
-		# check if template is not published
-		self.assertIsNone(frappe.db.exists("Website Item", {"item_code": variant.variant_of}))
-
-		variant_web_item = make_website_item(variant, save=False)
-		variant_web_item.save()
-
-		# check if template is published
-		try:
-			template_web_item = frappe.get_doc("Website Item", {"item_code": variant.variant_of})
-		except frappe.DoesNotExistError:
-			self.fail(f"Template of {variant.item_code}, {variant.variant_of} not published")
-
-		# teardown
-		variant_web_item.delete()
-		template_web_item.delete()
-		variant.delete()
-
-	def test_impact_on_merging_items(self):
-		"Check if merging items is blocked if old and new items both have website items"
-		first_item = make_item("Test First Item")
-		second_item = make_item("Test Second Item")
-
-		first_web_item = make_website_item(first_item, save=False)
-		first_web_item.save()
-		second_web_item = make_website_item(second_item, save=False)
-		second_web_item.save()
-
-		with self.assertRaises(DataValidationError):
-			frappe.rename_doc("Item", "Test First Item", "Test Second Item", merge=True)
-
-		# tear down
-		second_web_item.delete()
-		first_web_item.delete()
-		second_item.delete()
-		first_item.delete()
-
-	# Website Item Portal Tests Begin
-
-	def test_website_item_breadcrumbs(self):
-		"""
-		Check if breadcrumbs include homepage, product listing navigation page,
-		parent item group(s) and item group
-		"""
-		from erpnext.setup.doctype.item_group.item_group import get_parent_item_groups
-
-		item_code = "Test Breadcrumb Item"
-		item = make_item(
-			item_code,
-			{
-				"item_group": "_Test Item Group B - 1",
-			},
-		)
-
-		if not frappe.db.exists("Website Item", {"item_code": item_code}):
-			web_item = make_website_item(item, save=False)
-			web_item.save()
-		else:
-			web_item = frappe.get_cached_doc("Website Item", {"item_code": item_code})
-
-		frappe.db.set_value("Item Group", "_Test Item Group B - 1", "show_in_website", 1)
-		frappe.db.set_value("Item Group", "_Test Item Group B", "show_in_website", 1)
-
-		breadcrumbs = get_parent_item_groups(item.item_group)
-
-		settings = frappe.get_cached_doc("E Commerce Settings")
-		if settings.enable_field_filters:
-			base_breadcrumb = "Shop by Category"
-		else:
-			base_breadcrumb = "All Products"
-
-		self.assertEqual(breadcrumbs[0]["name"], "Home")
-		self.assertEqual(breadcrumbs[1]["name"], base_breadcrumb)
-		self.assertEqual(breadcrumbs[2]["name"], "_Test Item Group B")  # parent item group
-		self.assertEqual(breadcrumbs[3]["name"], "_Test Item Group B - 1")
-
-		# tear down
-		web_item.delete()
-		item.delete()
-
-	def test_website_item_price_for_logged_in_user(self):
-		"Check if price details are fetched correctly while logged in."
-		item_code = "Test Mobile Phone"
-
-		# show price in e commerce settings
-		setup_e_commerce_settings({"show_price": 1})
-
-		# price and pricing rule added via setUp
-
-		# login as customer with pricing rule
-		frappe.set_user("test_contact_customer@example.com")
-
-		# check if price and slashed price is fetched correctly
-		frappe.local.shopping_cart_settings = None
-		data = get_product_info_for_website(item_code, skip_quotation_creation=True)
-		self.assertTrue(bool(data.product_info["price"]))
-
-		price_object = data.product_info["price"]
-		self.assertEqual(price_object.get("discount_percent"), 25.0)
-		self.assertEqual(price_object.get("price_list_rate"), 750)
-		self.assertEqual(price_object.get("formatted_mrp"), "₹ 1,000.00")
-		self.assertEqual(price_object.get("formatted_price"), "₹ 750.00")
-		self.assertEqual(price_object.get("formatted_discount_percent"), "25.0%")
-
-		# switch to admin and disable show price
-		frappe.set_user("Administrator")
-		setup_e_commerce_settings({"show_price": 0})
-
-		# price should not be fetched for logged in user.
-		frappe.set_user("test_contact_customer@example.com")
-		frappe.local.shopping_cart_settings = None
-		data = get_product_info_for_website(item_code, skip_quotation_creation=True)
-		self.assertFalse(bool(data.product_info["price"]))
-
-		# tear down
-		frappe.set_user("Administrator")
-
-	def test_website_item_price_for_guest_user(self):
-		"Check if price details are fetched correctly for guest user."
-		item_code = "Test Mobile Phone"
-
-		# show price for guest user in e commerce settings
-		setup_e_commerce_settings({"show_price": 1, "hide_price_for_guest": 0})
-
-		# price and pricing rule added via setUp
-
-		# switch to guest user
-		frappe.set_user("Guest")
-
-		# price should be fetched
-		frappe.local.shopping_cart_settings = None
-		data = get_product_info_for_website(item_code, skip_quotation_creation=True)
-		self.assertTrue(bool(data.product_info["price"]))
-
-		price_object = data.product_info["price"]
-		self.assertEqual(price_object.get("discount_percent"), 10)
-		self.assertEqual(price_object.get("price_list_rate"), 900)
-
-		# hide price for guest user
-		frappe.set_user("Administrator")
-		setup_e_commerce_settings({"hide_price_for_guest": 1})
-		frappe.set_user("Guest")
-
-		# price should not be fetched
-		frappe.local.shopping_cart_settings = None
-		data = get_product_info_for_website(item_code, skip_quotation_creation=True)
-		self.assertFalse(bool(data.product_info["price"]))
-
-		# tear down
-		frappe.set_user("Administrator")
-
-	def test_website_item_stock_when_out_of_stock(self):
-		"""
-		Check if stock details are fetched correctly for empty inventory when:
-		1) Showing stock availability enabled:
-		        - Warehouse unset
-		        - Warehouse set
-		2) Showing stock availability disabled
-		"""
-		item_code = "Test Mobile Phone"
-		create_regular_web_item()
-		setup_e_commerce_settings({"show_stock_availability": 1})
-
-		frappe.local.shopping_cart_settings = None
-		data = get_product_info_for_website(item_code, skip_quotation_creation=True)
-
-		# check if stock details are fetched and item not in stock without warehouse set
-		self.assertFalse(bool(data.product_info["in_stock"]))
-		self.assertFalse(bool(data.product_info["stock_qty"]))
-
-		# set warehouse
-		frappe.db.set_value(
-			"Website Item", {"item_code": item_code}, "website_warehouse", "_Test Warehouse - _TC"
-		)
-
-		# check if stock details are fetched and item not in stock with warehouse set
-		data = get_product_info_for_website(item_code, skip_quotation_creation=True)
-		self.assertFalse(bool(data.product_info["in_stock"]))
-		self.assertEqual(data.product_info["stock_qty"], 0)
-
-		# disable show stock availability
-		setup_e_commerce_settings({"show_stock_availability": 0})
-		frappe.local.shopping_cart_settings = None
-		data = get_product_info_for_website(item_code, skip_quotation_creation=True)
-
-		# check if stock detail attributes are not fetched if stock availability is hidden
-		self.assertIsNone(data.product_info.get("in_stock"))
-		self.assertIsNone(data.product_info.get("stock_qty"))
-		self.assertIsNone(data.product_info.get("show_stock_qty"))
-
-		# tear down
-		frappe.get_cached_doc("Website Item", {"item_code": "Test Mobile Phone"}).delete()
-
-	def test_website_item_stock_when_in_stock(self):
-		"""
-		Check if stock details are fetched correctly for available inventory when:
-		1) Showing stock availability enabled:
-		        - Warehouse set
-		        - Warehouse unset
-		2) Showing stock availability disabled
-		"""
-		from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
-
-		item_code = "Test Mobile Phone"
-		create_regular_web_item()
-		setup_e_commerce_settings({"show_stock_availability": 1})
-		frappe.local.shopping_cart_settings = None
-
-		# set warehouse
-		frappe.db.set_value(
-			"Website Item", {"item_code": item_code}, "website_warehouse", "_Test Warehouse - _TC"
-		)
-
-		# stock up item
-		stock_entry = make_stock_entry(
-			item_code=item_code, target="_Test Warehouse - _TC", qty=2, rate=100
-		)
-
-		# check if stock details are fetched and item is in stock with warehouse set
-		data = get_product_info_for_website(item_code, skip_quotation_creation=True)
-		self.assertTrue(bool(data.product_info["in_stock"]))
-		self.assertEqual(data.product_info["stock_qty"], 2)
-
-		# unset warehouse
-		frappe.db.set_value("Website Item", {"item_code": item_code}, "website_warehouse", "")
-
-		# check if stock details are fetched and item not in stock without warehouse set
-		# (even though it has stock in some warehouse)
-		data = get_product_info_for_website(item_code, skip_quotation_creation=True)
-		self.assertFalse(bool(data.product_info["in_stock"]))
-		self.assertFalse(data.product_info["stock_qty"])
-
-		# disable show stock availability
-		setup_e_commerce_settings({"show_stock_availability": 0})
-		frappe.local.shopping_cart_settings = None
-		data = get_product_info_for_website(item_code, skip_quotation_creation=True)
-
-		# check if stock detail attributes are not fetched if stock availability is hidden
-		self.assertIsNone(data.product_info.get("in_stock"))
-		self.assertIsNone(data.product_info.get("stock_qty"))
-		self.assertIsNone(data.product_info.get("show_stock_qty"))
-
-		# tear down
-		stock_entry.cancel()
-		frappe.get_cached_doc("Website Item", {"item_code": "Test Mobile Phone"}).delete()
-
-	def test_recommended_item(self):
-		"Check if added recommended items are fetched correctly."
-		item_code = "Test Mobile Phone"
-		web_item = create_regular_web_item(item_code)
-
-		setup_e_commerce_settings({"enable_recommendations": 1, "show_price": 1})
-
-		# create recommended web item and price for it
-		recommended_web_item = create_regular_web_item("Test Mobile Phone 1")
-		make_web_item_price(item_code="Test Mobile Phone 1")
-
-		# add recommended item to first web item
-		web_item.append("recommended_items", {"website_item": recommended_web_item.name})
-		web_item.save()
-
-		frappe.local.shopping_cart_settings = None
-		e_commerce_settings = get_shopping_cart_settings()
-		recommended_items = web_item.get_recommended_items(e_commerce_settings)
-
-		# test results if show price is enabled
-		self.assertEqual(len(recommended_items), 1)
-		recomm_item = recommended_items[0]
-		self.assertEqual(recomm_item.get("website_item_name"), "Test Mobile Phone 1")
-		self.assertTrue(bool(recomm_item.get("price_info")))  # price fetched
-
-		price_info = recomm_item.get("price_info")
-		self.assertEqual(price_info.get("price_list_rate"), 1000)
-		self.assertEqual(price_info.get("formatted_price"), "₹ 1,000.00")
-
-		# test results if show price is disabled
-		setup_e_commerce_settings({"show_price": 0})
-
-		frappe.local.shopping_cart_settings = None
-		e_commerce_settings = get_shopping_cart_settings()
-		recommended_items = web_item.get_recommended_items(e_commerce_settings)
-
-		self.assertEqual(len(recommended_items), 1)
-		self.assertFalse(bool(recommended_items[0].get("price_info")))  # price not fetched
-
-		# tear down
-		web_item.delete()
-		recommended_web_item.delete()
-		frappe.get_cached_doc("Item", "Test Mobile Phone 1").delete()
-
-	def test_recommended_item_for_guest_user(self):
-		"Check if added recommended items are fetched correctly for guest user."
-		item_code = "Test Mobile Phone"
-		web_item = create_regular_web_item(item_code)
-
-		# price visible to guests
-		setup_e_commerce_settings(
-			{"enable_recommendations": 1, "show_price": 1, "hide_price_for_guest": 0}
-		)
-
-		# create recommended web item and price for it
-		recommended_web_item = create_regular_web_item("Test Mobile Phone 1")
-		make_web_item_price(item_code="Test Mobile Phone 1")
-
-		# add recommended item to first web item
-		web_item.append("recommended_items", {"website_item": recommended_web_item.name})
-		web_item.save()
-
-		frappe.set_user("Guest")
-
-		frappe.local.shopping_cart_settings = None
-		e_commerce_settings = get_shopping_cart_settings()
-		recommended_items = web_item.get_recommended_items(e_commerce_settings)
-
-		# test results if show price is enabled
-		self.assertEqual(len(recommended_items), 1)
-		self.assertTrue(bool(recommended_items[0].get("price_info")))  # price fetched
-
-		# price hidden from guests
-		frappe.set_user("Administrator")
-		setup_e_commerce_settings({"hide_price_for_guest": 1})
-		frappe.set_user("Guest")
-
-		frappe.local.shopping_cart_settings = None
-		e_commerce_settings = get_shopping_cart_settings()
-		recommended_items = web_item.get_recommended_items(e_commerce_settings)
-
-		# test results if show price is enabled
-		self.assertEqual(len(recommended_items), 1)
-		self.assertFalse(bool(recommended_items[0].get("price_info")))  # price fetched
-
-		# tear down
-		frappe.set_user("Administrator")
-		web_item.delete()
-		recommended_web_item.delete()
-		frappe.get_cached_doc("Item", "Test Mobile Phone 1").delete()
-
-
-def create_regular_web_item(item_code=None, item_args=None, web_args=None):
-	"Create Regular Item and Website Item."
-	item_code = item_code or "Test Mobile Phone"
-	item = make_item(item_code, properties=item_args)
-
-	if not frappe.db.exists("Website Item", {"item_code": item_code}):
-		web_item = make_website_item(item, save=False)
-		if web_args:
-			web_item.update(web_args)
-		web_item.save()
-	else:
-		web_item = frappe.get_cached_doc("Website Item", {"item_code": item_code})
-
-	return web_item
-
-
-def make_web_item_price(**kwargs):
-	item_code = kwargs.get("item_code")
-	if not item_code:
-		return
-
-	if not frappe.db.exists("Item Price", {"item_code": item_code}):
-		item_price = frappe.get_doc(
-			{
-				"doctype": "Item Price",
-				"item_code": item_code,
-				"price_list": kwargs.get("price_list") or "_Test Price List India",
-				"price_list_rate": kwargs.get("price_list_rate") or 1000,
-			}
-		)
-		item_price.insert()
-	else:
-		item_price = frappe.get_cached_doc("Item Price", {"item_code": item_code})
-
-	return item_price
-
-
-def make_web_pricing_rule(**kwargs):
-	title = kwargs.get("title")
-	if not title:
-		return
-
-	if not frappe.db.exists("Pricing Rule", title):
-		pricing_rule = frappe.get_doc(
-			{
-				"doctype": "Pricing Rule",
-				"title": title,
-				"apply_on": kwargs.get("apply_on") or "Item Code",
-				"items": [{"item_code": kwargs.get("item_code")}],
-				"selling": kwargs.get("selling") or 0,
-				"buying": kwargs.get("buying") or 0,
-				"rate_or_discount": kwargs.get("rate_or_discount") or "Discount Percentage",
-				"discount_percentage": kwargs.get("discount_percentage") or 10,
-				"company": kwargs.get("company") or "_Test Company",
-				"currency": kwargs.get("currency") or "INR",
-				"for_price_list": kwargs.get("price_list") or "_Test Price List India",
-				"applicable_for": kwargs.get("applicable_for") or "",
-				"customer": kwargs.get("customer") or "",
-			}
-		)
-		pricing_rule.insert()
-	else:
-		pricing_rule = frappe.get_doc("Pricing Rule", {"title": title})
-
-	return pricing_rule
-
-
-def create_user_and_customer_if_not_exists(email, first_name=None):
-	if frappe.db.exists("User", email):
-		return
-
-	frappe.get_doc(
-		{
-			"doctype": "User",
-			"user_type": "Website User",
-			"email": email,
-			"send_welcome_email": 0,
-			"first_name": first_name or email.split("@")[0],
-		}
-	).insert(ignore_permissions=True)
-
-	contact = frappe.get_last_doc("Contact", filters={"email_id": email})
-	link = contact.append("links", {})
-	link.link_doctype = "Customer"
-	link.link_name = "_Test Customer"
-	link.link_title = "_Test Customer"
-	contact.save()
-
-
-test_dependencies = ["Price List", "Item Price", "Customer", "Contact", "Item"]
diff --git a/erpnext/e_commerce/doctype/website_item/website_item.js b/erpnext/e_commerce/doctype/website_item/website_item.js
deleted file mode 100644
index b6595cc..0000000
--- a/erpnext/e_commerce/doctype/website_item/website_item.js
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Website Item', {
-	onload: (frm) => {
-		// should never check Private
-		frm.fields_dict["website_image"].df.is_private = 0;
-	},
-
-	refresh: (frm) => {
-		frm.add_custom_button(__("Prices"), function() {
-			frappe.set_route("List", "Item Price", {"item_code": frm.doc.item_code});
-		}, __("View"));
-
-		frm.add_custom_button(__("Stock"), function() {
-			frappe.route_options = {
-				"item_code": frm.doc.item_code
-			};
-			frappe.set_route("query-report", "Stock Balance");
-		}, __("View"));
-
-		frm.add_custom_button(__("E Commerce Settings"), function() {
-			frappe.set_route("Form", "E Commerce Settings");
-		}, __("View"));
-	},
-
-	copy_from_item_group: (frm) => {
-		return frm.call({
-			doc: frm.doc,
-			method: "copy_specification_from_item_group"
-		});
-	},
-
-	set_meta_tags: (frm) => {
-		frappe.utils.set_meta_tag(frm.doc.route);
-	}
-});
diff --git a/erpnext/e_commerce/doctype/website_item/website_item.json b/erpnext/e_commerce/doctype/website_item/website_item.json
deleted file mode 100644
index 6f551a0..0000000
--- a/erpnext/e_commerce/doctype/website_item/website_item.json
+++ /dev/null
@@ -1,414 +0,0 @@
-{
- "actions": [],
- "allow_guest_to_view": 1,
- "allow_import": 1,
- "autoname": "naming_series",
- "creation": "2021-02-09 21:06:14.441698",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "naming_series",
-  "web_item_name",
-  "route",
-  "has_variants",
-  "variant_of",
-  "published",
-  "column_break_3",
-  "item_code",
-  "item_name",
-  "item_group",
-  "stock_uom",
-  "column_break_11",
-  "description",
-  "brand",
-  "display_section",
-  "website_image",
-  "website_image_alt",
-  "column_break_13",
-  "slideshow",
-  "thumbnail",
-  "stock_information_section",
-  "website_warehouse",
-  "column_break_24",
-  "on_backorder",
-  "section_break_17",
-  "short_description",
-  "web_long_description",
-  "column_break_27",
-  "website_specifications",
-  "copy_from_item_group",
-  "display_additional_information_section",
-  "show_tabbed_section",
-  "tabs",
-  "recommended_items_section",
-  "recommended_items",
-  "offers_section",
-  "offers",
-  "section_break_6",
-  "ranking",
-  "set_meta_tags",
-  "column_break_22",
-  "website_item_groups",
-  "advanced_display_section",
-  "website_content"
- ],
- "fields": [
-  {
-   "description": "Website display name",
-   "fetch_from": "item_code.item_name",
-   "fetch_if_empty": 1,
-   "fieldname": "web_item_name",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Website Item Name",
-   "reqd": 1
-  },
-  {
-   "fieldname": "column_break_3",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "item_code",
-   "fieldtype": "Link",
-   "label": "Item Code",
-   "options": "Item",
-   "read_only_depends_on": "eval:!doc.__islocal",
-   "reqd": 1
-  },
-  {
-   "fetch_from": "item_code.item_name",
-   "fieldname": "item_name",
-   "fieldtype": "Data",
-   "label": "Item Name",
-   "read_only": 1
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "section_break_6",
-   "fieldtype": "Section Break",
-   "label": "Search and SEO"
-  },
-  {
-   "fieldname": "route",
-   "fieldtype": "Small Text",
-   "in_list_view": 1,
-   "label": "Route",
-   "no_copy": 1
-  },
-  {
-   "description": "Items with higher ranking will be shown higher",
-   "fieldname": "ranking",
-   "fieldtype": "Int",
-   "label": "Ranking"
-  },
-  {
-   "description": "Show a slideshow at the top of the page",
-   "fieldname": "slideshow",
-   "fieldtype": "Link",
-   "label": "Slideshow",
-   "options": "Website Slideshow"
-  },
-  {
-   "description": "Item Image (if not slideshow)",
-   "fieldname": "website_image",
-   "fieldtype": "Attach Image",
-   "hidden": 1,
-   "in_preview": 1,
-   "label": "Website Image",
-   "print_hide": 1
-  },
-  {
-   "description": "Image Alternative Text",
-   "fieldname": "website_image_alt",
-   "fieldtype": "Data",
-   "label": "Image Description"
-  },
-  {
-   "fieldname": "thumbnail",
-   "fieldtype": "Data",
-   "label": "Thumbnail",
-   "read_only": 1
-  },
-  {
-   "fieldname": "column_break_13",
-   "fieldtype": "Column Break"
-  },
-  {
-   "description": "Show Stock availability based on this warehouse. If the parent warehouse is selected, then the system will display the consolidated available quantity of all child warehouses.",
-   "fieldname": "website_warehouse",
-   "fieldtype": "Link",
-   "ignore_user_permissions": 1,
-   "label": "Website Warehouse",
-   "options": "Warehouse"
-  },
-  {
-   "description": "List this Item in multiple groups on the website.",
-   "fieldname": "website_item_groups",
-   "fieldtype": "Table",
-   "label": "Website Item Groups",
-   "options": "Website Item Group"
-  },
-  {
-   "fieldname": "set_meta_tags",
-   "fieldtype": "Button",
-   "label": "Set Meta Tags"
-  },
-  {
-   "fieldname": "section_break_17",
-   "fieldtype": "Section Break",
-   "label": "Display Information"
-  },
-  {
-   "fieldname": "copy_from_item_group",
-   "fieldtype": "Button",
-   "label": "Copy From Item Group"
-  },
-  {
-   "fieldname": "website_specifications",
-   "fieldtype": "Table",
-   "label": "Website Specifications",
-   "options": "Item Website Specification"
-  },
-  {
-   "fieldname": "web_long_description",
-   "fieldtype": "Text Editor",
-   "label": "Website Description"
-  },
-  {
-   "description": "You can use any valid Bootstrap 4 markup in this field. It will be shown on your Item Page.",
-   "fieldname": "website_content",
-   "fieldtype": "HTML Editor",
-   "label": "Website Content"
-  },
-  {
-   "fetch_from": "item_code.item_group",
-   "fieldname": "item_group",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Item Group",
-   "options": "Item Group",
-   "read_only": 1,
-   "search_index": 1
-  },
-  {
-   "default": "1",
-   "fieldname": "published",
-   "fieldtype": "Check",
-   "label": "Published"
-  },
-  {
-   "default": "0",
-   "depends_on": "has_variants",
-   "fetch_from": "item_code.has_variants",
-   "fieldname": "has_variants",
-   "fieldtype": "Check",
-   "in_standard_filter": 1,
-   "label": "Has Variants",
-   "no_copy": 1,
-   "read_only": 1
-  },
-  {
-   "depends_on": "variant_of",
-   "fetch_from": "item_code.variant_of",
-   "fieldname": "variant_of",
-   "fieldtype": "Link",
-   "ignore_user_permissions": 1,
-   "in_standard_filter": 1,
-   "label": "Variant Of",
-   "options": "Item",
-   "read_only": 1,
-   "search_index": 1,
-   "set_only_once": 1
-  },
-  {
-   "fetch_from": "item_code.stock_uom",
-   "fieldname": "stock_uom",
-   "fieldtype": "Link",
-   "label": "Stock UOM",
-   "options": "UOM",
-   "read_only": 1
-  },
-  {
-   "depends_on": "brand",
-   "fetch_from": "item_code.brand",
-   "fieldname": "brand",
-   "fieldtype": "Link",
-   "label": "Brand",
-   "options": "Brand",
-   "search_index": 1
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "advanced_display_section",
-   "fieldtype": "Section Break",
-   "label": "Advanced Display Content"
-  },
-  {
-   "fieldname": "display_section",
-   "fieldtype": "Section Break",
-   "label": "Display Images"
-  },
-  {
-   "fieldname": "column_break_27",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "column_break_22",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fetch_from": "item_code.description",
-   "fieldname": "description",
-   "fieldtype": "Text Editor",
-   "label": "Item Description",
-   "read_only": 1
-  },
-  {
-   "default": "WEB-ITM-.####",
-   "fieldname": "naming_series",
-   "fieldtype": "Select",
-   "hidden": 1,
-   "label": "Naming Series",
-   "no_copy": 1,
-   "options": "WEB-ITM-.####",
-   "print_hide": 1
-  },
-  {
-   "fieldname": "display_additional_information_section",
-   "fieldtype": "Section Break",
-   "label": "Display Additional Information"
-  },
-  {
-   "depends_on": "show_tabbed_section",
-   "fieldname": "tabs",
-   "fieldtype": "Table",
-   "label": "Tabs",
-   "options": "Website Item Tabbed Section"
-  },
-  {
-   "default": "0",
-   "fieldname": "show_tabbed_section",
-   "fieldtype": "Check",
-   "label": "Add Section with Tabs"
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "offers_section",
-   "fieldtype": "Section Break",
-   "label": "Offers"
-  },
-  {
-   "fieldname": "offers",
-   "fieldtype": "Table",
-   "label": "Offers to Display",
-   "options": "Website Offer"
-  },
-  {
-   "fieldname": "column_break_11",
-   "fieldtype": "Column Break"
-  },
-  {
-   "description": "Short Description for List View",
-   "fieldname": "short_description",
-   "fieldtype": "Small Text",
-   "label": "Short Website Description"
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "recommended_items_section",
-   "fieldtype": "Section Break",
-   "label": "Recommended Items"
-  },
-  {
-   "fieldname": "recommended_items",
-   "fieldtype": "Table",
-   "label": "Recommended/Similar Items",
-   "options": "Recommended Items"
-  },
-  {
-   "fieldname": "stock_information_section",
-   "fieldtype": "Section Break",
-   "label": "Stock Information"
-  },
-  {
-   "fieldname": "column_break_24",
-   "fieldtype": "Column Break"
-  },
-  {
-   "default": "0",
-   "description": "Indicate that Item is available on backorder and not usually pre-stocked",
-   "fieldname": "on_backorder",
-   "fieldtype": "Check",
-   "label": "On Backorder"
-  }
- ],
- "has_web_view": 1,
- "image_field": "website_image",
- "index_web_pages_for_search": 1,
- "links": [],
- "make_attachments_public": 1,
- "modified": "2023-09-12 14:19:22.822689",
- "modified_by": "Administrator",
- "module": "E-commerce",
- "name": "Website Item",
- "naming_rule": "Expression (old style)",
- "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": "Website Manager",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Stock User",
-   "share": 1,
-   "write": 1
-  },
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Stock Manager",
-   "share": 1,
-   "write": 1
-  }
- ],
- "search_fields": "web_item_name, item_code, item_group",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "states": [],
- "title_field": "web_item_name",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/e_commerce/doctype/website_item/website_item.py b/erpnext/e_commerce/doctype/website_item/website_item.py
deleted file mode 100644
index 81b8eca..0000000
--- a/erpnext/e_commerce/doctype/website_item/website_item.py
+++ /dev/null
@@ -1,469 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-import json
-from typing import TYPE_CHECKING, List, Union
-
-if TYPE_CHECKING:
-	from erpnext.stock.doctype.item.item import Item
-
-import frappe
-from frappe import _
-from frappe.utils import cint, cstr, flt, random_string
-from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow
-from frappe.website.website_generator import WebsiteGenerator
-
-from erpnext.e_commerce.doctype.item_review.item_review import get_item_reviews
-from erpnext.e_commerce.redisearch_utils import (
-	delete_item_from_index,
-	insert_item_to_index,
-	update_index_for_item,
-)
-from erpnext.e_commerce.shopping_cart.cart import _set_price_list
-from erpnext.setup.doctype.item_group.item_group import (
-	get_parent_item_groups,
-	invalidate_cache_for,
-)
-from erpnext.utilities.product import get_price
-
-
-class WebsiteItem(WebsiteGenerator):
-	website = frappe._dict(
-		page_title_field="web_item_name",
-		condition_field="published",
-		template="templates/generators/item/item.html",
-		no_cache=1,
-	)
-
-	def autoname(self):
-		# use naming series to accomodate items with same name (different item code)
-		from frappe.model.naming import get_default_naming_series, make_autoname
-
-		naming_series = get_default_naming_series("Website Item")
-		if not self.name and naming_series:
-			self.name = make_autoname(naming_series, doc=self)
-
-	def onload(self):
-		super(WebsiteItem, self).onload()
-
-	def validate(self):
-		super(WebsiteItem, self).validate()
-
-		if not self.item_code:
-			frappe.throw(_("Item Code is required"), title=_("Mandatory"))
-
-		self.validate_duplicate_website_item()
-		self.validate_website_image()
-		self.make_thumbnail()
-		self.publish_unpublish_desk_item(publish=True)
-
-		if not self.get("__islocal"):
-			wig = frappe.qb.DocType("Website Item Group")
-			query = (
-				frappe.qb.from_(wig)
-				.select(wig.item_group)
-				.where(
-					(wig.parentfield == "website_item_groups")
-					& (wig.parenttype == "Website Item")
-					& (wig.parent == self.name)
-				)
-			)
-			result = query.run(as_list=True)
-
-			self.old_website_item_groups = [x[0] for x in result]
-
-	def on_update(self):
-		invalidate_cache_for_web_item(self)
-		self.update_template_item()
-
-	def on_trash(self):
-		super(WebsiteItem, self).on_trash()
-		delete_item_from_index(self)
-		self.publish_unpublish_desk_item(publish=False)
-
-	def validate_duplicate_website_item(self):
-		existing_web_item = frappe.db.exists("Website Item", {"item_code": self.item_code})
-		if existing_web_item and existing_web_item != self.name:
-			message = _("Website Item already exists against Item {0}").format(frappe.bold(self.item_code))
-			frappe.throw(message, title=_("Already Published"))
-
-	def publish_unpublish_desk_item(self, publish=True):
-		if frappe.db.get_value("Item", self.item_code, "published_in_website") and publish:
-			return  # if already published don't publish again
-		frappe.db.set_value("Item", self.item_code, "published_in_website", publish)
-
-	def make_route(self):
-		"""Called from set_route in WebsiteGenerator."""
-		if not self.route:
-			return (
-				cstr(frappe.db.get_value("Item Group", self.item_group, "route"))
-				+ "/"
-				+ self.scrub((self.item_name if self.item_name else self.item_code) + "-" + random_string(5))
-			)
-
-	def update_template_item(self):
-		"""Publish Template Item if Variant is published."""
-		if self.variant_of:
-			if self.published:
-				# show template
-				template_item = frappe.get_doc("Item", self.variant_of)
-
-				if not template_item.published_in_website:
-					template_item.flags.ignore_permissions = True
-					make_website_item(template_item)
-
-	def validate_website_image(self):
-		if frappe.flags.in_import:
-			return
-
-		"""Validate if the website image is a public file"""
-		if not self.website_image:
-			return
-
-		# find if website image url exists as public
-		file_doc = frappe.get_all(
-			"File",
-			filters={"file_url": self.website_image},
-			fields=["name", "is_private"],
-			order_by="is_private asc",
-			limit_page_length=1,
-		)
-
-		if file_doc:
-			file_doc = file_doc[0]
-
-		if not file_doc:
-			frappe.msgprint(
-				_("Website Image {0} attached to Item {1} cannot be found").format(
-					self.website_image, self.name
-				)
-			)
-
-			self.website_image = None
-
-		elif file_doc.is_private:
-			frappe.msgprint(_("Website Image should be a public file or website URL"))
-
-			self.website_image = None
-
-	def make_thumbnail(self):
-		"""Make a thumbnail of `website_image`"""
-		if frappe.flags.in_import or frappe.flags.in_migrate:
-			return
-
-		import requests.exceptions
-
-		db_website_image = frappe.db.get_value(self.doctype, self.name, "website_image")
-		if not self.is_new() and self.website_image != db_website_image:
-			self.thumbnail = None
-
-		if self.website_image and not self.thumbnail:
-			file_doc = None
-
-			try:
-				file_doc = frappe.get_doc(
-					"File",
-					{
-						"file_url": self.website_image,
-						"attached_to_doctype": "Website Item",
-						"attached_to_name": self.name,
-					},
-				)
-			except frappe.DoesNotExistError:
-				pass
-				# cleanup
-				frappe.local.message_log.pop()
-
-			except requests.exceptions.HTTPError:
-				frappe.msgprint(_("Warning: Invalid attachment {0}").format(self.website_image))
-				self.website_image = None
-
-			except requests.exceptions.SSLError:
-				frappe.msgprint(
-					_("Warning: Invalid SSL certificate on attachment {0}").format(self.website_image)
-				)
-				self.website_image = None
-
-			# for CSV import
-			if self.website_image and not file_doc:
-				try:
-					file_doc = frappe.get_doc(
-						{
-							"doctype": "File",
-							"file_url": self.website_image,
-							"attached_to_doctype": "Website Item",
-							"attached_to_name": self.name,
-						}
-					).save()
-
-				except IOError:
-					self.website_image = None
-
-			if file_doc:
-				if not file_doc.thumbnail_url:
-					file_doc.make_thumbnail()
-
-				self.thumbnail = file_doc.thumbnail_url
-
-	def get_context(self, context):
-		context.show_search = True
-		context.search_link = "/search"
-		context.body_class = "product-page"
-
-		context.parents = get_parent_item_groups(self.item_group, from_item=True)  # breadcumbs
-		self.attributes = frappe.get_all(
-			"Item Variant Attribute",
-			fields=["attribute", "attribute_value"],
-			filters={"parent": self.item_code},
-		)
-
-		if self.slideshow:
-			context.update(get_slideshow(self))
-
-		self.set_metatags(context)
-		self.set_shopping_cart_data(context)
-
-		settings = context.shopping_cart.cart_settings
-
-		self.get_product_details_section(context)
-
-		if settings.get("enable_reviews"):
-			reviews_data = get_item_reviews(self.name)
-			context.update(reviews_data)
-			context.reviews = context.reviews[:4]
-
-		context.wished = False
-		if frappe.db.exists(
-			"Wishlist Item", {"item_code": self.item_code, "parent": frappe.session.user}
-		):
-			context.wished = True
-
-		context.user_is_customer = check_if_user_is_customer()
-
-		context.recommended_items = None
-		if settings and settings.enable_recommendations:
-			context.recommended_items = self.get_recommended_items(settings)
-
-		return context
-
-	def set_selected_attributes(self, variants, context, attribute_values_available):
-		for variant in variants:
-			variant.attributes = frappe.get_all(
-				"Item Variant Attribute",
-				filters={"parent": variant.name},
-				fields=["attribute", "attribute_value as value"],
-			)
-
-			# make an attribute-value map for easier access in templates
-			variant.attribute_map = frappe._dict(
-				{attr.attribute: attr.value for attr in variant.attributes}
-			)
-
-			for attr in variant.attributes:
-				values = attribute_values_available.setdefault(attr.attribute, [])
-				if attr.value not in values:
-					values.append(attr.value)
-
-				if variant.name == context.variant.name:
-					context.selected_attributes[attr.attribute] = attr.value
-
-	def set_attribute_values(self, attributes, context, attribute_values_available):
-		for attr in attributes:
-			values = context.attribute_values.setdefault(attr.attribute, [])
-
-			if cint(frappe.db.get_value("Item Attribute", attr.attribute, "numeric_values")):
-				for val in sorted(attribute_values_available.get(attr.attribute, []), key=flt):
-					values.append(val)
-			else:
-				# get list of values defined (for sequence)
-				for attr_value in frappe.db.get_all(
-					"Item Attribute Value",
-					fields=["attribute_value"],
-					filters={"parent": attr.attribute},
-					order_by="idx asc",
-				):
-
-					if attr_value.attribute_value in attribute_values_available.get(attr.attribute, []):
-						values.append(attr_value.attribute_value)
-
-	def set_metatags(self, context):
-		context.metatags = frappe._dict({})
-
-		safe_description = frappe.utils.to_markdown(self.description)
-
-		context.metatags.url = frappe.utils.get_url() + "/" + context.route
-
-		if context.website_image:
-			if context.website_image.startswith("http"):
-				url = context.website_image
-			else:
-				url = frappe.utils.get_url() + context.website_image
-			context.metatags.image = url
-
-		context.metatags.description = safe_description[:300]
-
-		context.metatags.title = self.web_item_name or self.item_name or self.item_code
-
-		context.metatags["og:type"] = "product"
-		context.metatags["og:site_name"] = "ERPNext"
-
-	def set_shopping_cart_data(self, context):
-		from erpnext.e_commerce.shopping_cart.product_info import get_product_info_for_website
-
-		context.shopping_cart = get_product_info_for_website(
-			self.item_code, skip_quotation_creation=True
-		)
-
-	@frappe.whitelist()
-	def copy_specification_from_item_group(self):
-		self.set("website_specifications", [])
-		if self.item_group:
-			for label, desc in frappe.db.get_values(
-				"Item Website Specification", {"parent": self.item_group}, ["label", "description"]
-			):
-				row = self.append("website_specifications")
-				row.label = label
-				row.description = desc
-
-	def get_product_details_section(self, context):
-		"""Get section with tabs or website specifications."""
-		context.show_tabs = self.show_tabbed_section
-		if self.show_tabbed_section and (self.tabs or self.website_specifications):
-			context.tabs = self.get_tabs()
-		else:
-			context.website_specifications = self.website_specifications
-
-	def get_tabs(self):
-		tab_values = {}
-		tab_values["tab_1_title"] = "Product Details"
-		tab_values["tab_1_content"] = frappe.render_template(
-			"templates/generators/item/item_specifications.html",
-			{"website_specifications": self.website_specifications, "show_tabs": self.show_tabbed_section},
-		)
-
-		for row in self.tabs:
-			tab_values[f"tab_{row.idx + 1}_title"] = _(row.label)
-			tab_values[f"tab_{row.idx + 1}_content"] = row.content
-
-		return tab_values
-
-	def get_recommended_items(self, settings):
-		ri = frappe.qb.DocType("Recommended Items")
-		wi = frappe.qb.DocType("Website Item")
-
-		query = (
-			frappe.qb.from_(ri)
-			.join(wi)
-			.on(ri.item_code == wi.item_code)
-			.select(ri.item_code, ri.route, ri.website_item_name, ri.website_item_thumbnail)
-			.where((ri.parent == self.name) & (wi.published == 1))
-			.orderby(ri.idx)
-		)
-		items = query.run(as_dict=True)
-
-		if settings.show_price:
-			is_guest = frappe.session.user == "Guest"
-			# Show Price if logged in.
-			# If not logged in and price is hidden for guest, skip price fetch.
-			if is_guest and settings.hide_price_for_guest:
-				return items
-
-			selling_price_list = _set_price_list(settings, None)
-			for item in items:
-				item.price_info = get_price(
-					item.item_code, selling_price_list, settings.default_customer_group, settings.company
-				)
-
-		return items
-
-
-def invalidate_cache_for_web_item(doc):
-	"""Invalidate Website Item Group cache and rebuild ItemVariantsCacheManager."""
-	from erpnext.stock.doctype.item.item import invalidate_item_variants_cache_for_website
-
-	invalidate_cache_for(doc, doc.item_group)
-
-	website_item_groups = list(
-		set(
-			(doc.get("old_website_item_groups") or [])
-			+ [d.item_group for d in doc.get({"doctype": "Website Item Group"}) if d.item_group]
-		)
-	)
-
-	for item_group in website_item_groups:
-		invalidate_cache_for(doc, item_group)
-
-	# Update Search Cache
-	update_index_for_item(doc)
-
-	invalidate_item_variants_cache_for_website(doc)
-
-
-def on_doctype_update():
-	# since route is a Text column, it needs a length for indexing
-	frappe.db.add_index("Website Item", ["route(500)"])
-
-
-def check_if_user_is_customer(user=None):
-	from frappe.contacts.doctype.contact.contact import get_contact_name
-
-	if not user:
-		user = frappe.session.user
-
-	contact_name = get_contact_name(user)
-	customer = None
-
-	if contact_name:
-		contact = frappe.get_doc("Contact", contact_name)
-		for link in contact.links:
-			if link.link_doctype == "Customer":
-				customer = link.link_name
-				break
-
-	return True if customer else False
-
-
-@frappe.whitelist()
-def make_website_item(doc: "Item", save: bool = True) -> Union["WebsiteItem", List[str]]:
-	"Make Website Item from Item. Used via Form UI or patch."
-
-	if not doc:
-		return
-
-	if isinstance(doc, str):
-		doc = json.loads(doc)
-
-	if frappe.db.exists("Website Item", {"item_code": doc.get("item_code")}):
-		message = _("Website Item already exists against {0}").format(frappe.bold(doc.get("item_code")))
-		frappe.throw(message, title=_("Already Published"))
-
-	website_item = frappe.new_doc("Website Item")
-	website_item.web_item_name = doc.get("item_name")
-
-	fields_to_map = [
-		"item_code",
-		"item_name",
-		"item_group",
-		"stock_uom",
-		"brand",
-		"has_variants",
-		"variant_of",
-		"description",
-	]
-	for field in fields_to_map:
-		website_item.update({field: doc.get(field)})
-
-	# Needed for publishing/mapping via Form UI only
-	if not frappe.flags.in_migrate and (doc.get("image") and not website_item.website_image):
-		website_item.website_image = doc.get("image")
-
-	if not save:
-		return website_item
-
-	website_item.save()
-
-	# Add to search cache
-	insert_item_to_index(website_item)
-
-	return [website_item.name, website_item.web_item_name]
diff --git a/erpnext/e_commerce/doctype/website_item/website_item_list.js b/erpnext/e_commerce/doctype/website_item/website_item_list.js
deleted file mode 100644
index b9dd921..0000000
--- a/erpnext/e_commerce/doctype/website_item/website_item_list.js
+++ /dev/null
@@ -1,20 +0,0 @@
-frappe.listview_settings['Website Item'] = {
-	add_fields: ["item_name", "web_item_name", "published", "website_image", "has_variants", "variant_of"],
-	filters: [["published", "=", "1"]],
-
-	get_indicator: function(doc) {
-		if (doc.has_variants && doc.published) {
-			return [__("Template"), "orange", "has_variants,=,Yes|published,=,1"];
-		} else if (doc.has_variants && !doc.published) {
-			return [__("Template"), "grey", "has_variants,=,Yes|published,=,0"];
-		} else if (doc.variant_of  && doc.published) {
-			return [__("Variant"), "blue", "published,=,1|variant_of,=," + doc.variant_of];
-		} else if (doc.variant_of  && !doc.published) {
-			return [__("Variant"), "grey", "published,=,0|variant_of,=," + doc.variant_of];
-		} else if (doc.published) {
-			return [__("Published"), "green", "published,=,1"];
-		} else {
-			return [__("Not Published"), "grey", "published,=,0"];
-		}
-	}
-};
\ No newline at end of file
diff --git a/erpnext/e_commerce/doctype/website_item_tabbed_section/__init__.py b/erpnext/e_commerce/doctype/website_item_tabbed_section/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/e_commerce/doctype/website_item_tabbed_section/__init__.py
+++ /dev/null
diff --git a/erpnext/e_commerce/doctype/website_item_tabbed_section/website_item_tabbed_section.json b/erpnext/e_commerce/doctype/website_item_tabbed_section/website_item_tabbed_section.json
deleted file mode 100644
index 6601dd8..0000000
--- a/erpnext/e_commerce/doctype/website_item_tabbed_section/website_item_tabbed_section.json
+++ /dev/null
@@ -1,37 +0,0 @@
-{
- "actions": [],
- "creation": "2021-03-18 20:32:15.321402",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "label",
-  "content"
- ],
- "fields": [
-  {
-   "fieldname": "label",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Label"
-  },
-  {
-   "fieldname": "content",
-   "fieldtype": "HTML Editor",
-   "in_list_view": 1,
-   "label": "Content"
-  }
- ],
- "index_web_pages_for_search": 1,
- "istable": 1,
- "links": [],
- "modified": "2021-03-18 20:35:26.991192",
- "modified_by": "Administrator",
- "module": "E-commerce",
- "name": "Website Item Tabbed Section",
- "owner": "Administrator",
- "permissions": [],
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/e_commerce/doctype/website_item_tabbed_section/website_item_tabbed_section.py b/erpnext/e_commerce/doctype/website_item_tabbed_section/website_item_tabbed_section.py
deleted file mode 100644
index 91148b8..0000000
--- a/erpnext/e_commerce/doctype/website_item_tabbed_section/website_item_tabbed_section.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-# import frappe
-from frappe.model.document import Document
-
-
-class WebsiteItemTabbedSection(Document):
-	pass
diff --git a/erpnext/e_commerce/doctype/website_offer/__init__.py b/erpnext/e_commerce/doctype/website_offer/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/e_commerce/doctype/website_offer/__init__.py
+++ /dev/null
diff --git a/erpnext/e_commerce/doctype/website_offer/website_offer.json b/erpnext/e_commerce/doctype/website_offer/website_offer.json
deleted file mode 100644
index 627d548..0000000
--- a/erpnext/e_commerce/doctype/website_offer/website_offer.json
+++ /dev/null
@@ -1,43 +0,0 @@
-{
- "actions": [],
- "creation": "2021-04-21 13:37:14.162162",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "offer_title",
-  "offer_subtitle",
-  "offer_details"
- ],
- "fields": [
-  {
-   "fieldname": "offer_title",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Offer Title"
-  },
-  {
-   "fieldname": "offer_subtitle",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Offer Subtitle"
-  },
-  {
-   "fieldname": "offer_details",
-   "fieldtype": "Text Editor",
-   "label": "Offer Details"
-  }
- ],
- "index_web_pages_for_search": 1,
- "istable": 1,
- "links": [],
- "modified": "2021-04-21 13:56:04.660331",
- "modified_by": "Administrator",
- "module": "E-commerce",
- "name": "Website Offer",
- "owner": "Administrator",
- "permissions": [],
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/e_commerce/doctype/website_offer/website_offer.py b/erpnext/e_commerce/doctype/website_offer/website_offer.py
deleted file mode 100644
index 8c92f75..0000000
--- a/erpnext/e_commerce/doctype/website_offer/website_offer.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-import frappe
-from frappe.model.document import Document
-
-
-class WebsiteOffer(Document):
-	pass
-
-
-@frappe.whitelist(allow_guest=True)
-def get_offer_details(offer_id):
-	return frappe.db.get_value("Website Offer", {"name": offer_id}, ["offer_details"])
diff --git a/erpnext/e_commerce/doctype/wishlist/__init__.py b/erpnext/e_commerce/doctype/wishlist/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/e_commerce/doctype/wishlist/__init__.py
+++ /dev/null
diff --git a/erpnext/e_commerce/doctype/wishlist/test_wishlist.py b/erpnext/e_commerce/doctype/wishlist/test_wishlist.py
deleted file mode 100644
index 9d27126..0000000
--- a/erpnext/e_commerce/doctype/wishlist/test_wishlist.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-import unittest
-
-import frappe
-from frappe.core.doctype.user_permission.test_user_permission import create_user
-
-from erpnext.e_commerce.doctype.website_item.website_item import make_website_item
-from erpnext.e_commerce.doctype.wishlist.wishlist import add_to_wishlist, remove_from_wishlist
-from erpnext.stock.doctype.item.test_item import make_item
-
-
-class TestWishlist(unittest.TestCase):
-	def setUp(self):
-		item = make_item("Test Phone Series X")
-		if not frappe.db.exists("Website Item", {"item_code": "Test Phone Series X"}):
-			make_website_item(item, save=True)
-
-		item = make_item("Test Phone Series Y")
-		if not frappe.db.exists("Website Item", {"item_code": "Test Phone Series Y"}):
-			make_website_item(item, save=True)
-
-	def tearDown(self):
-		frappe.get_cached_doc("Website Item", {"item_code": "Test Phone Series X"}).delete()
-		frappe.get_cached_doc("Website Item", {"item_code": "Test Phone Series Y"}).delete()
-		frappe.get_cached_doc("Item", "Test Phone Series X").delete()
-		frappe.get_cached_doc("Item", "Test Phone Series Y").delete()
-
-	def test_add_remove_items_in_wishlist(self):
-		"Check if items are added and removed from user's wishlist."
-		# add first item
-		add_to_wishlist("Test Phone Series X")
-
-		# check if wishlist was created and item was added
-		self.assertTrue(frappe.db.exists("Wishlist", {"user": frappe.session.user}))
-		self.assertTrue(
-			frappe.db.exists(
-				"Wishlist Item", {"item_code": "Test Phone Series X", "parent": frappe.session.user}
-			)
-		)
-
-		# add second item to wishlist
-		add_to_wishlist("Test Phone Series Y")
-		wishlist_length = frappe.db.get_value(
-			"Wishlist Item", {"parent": frappe.session.user}, "count(*)"
-		)
-		self.assertEqual(wishlist_length, 2)
-
-		remove_from_wishlist("Test Phone Series X")
-		remove_from_wishlist("Test Phone Series Y")
-
-		wishlist_length = frappe.db.get_value(
-			"Wishlist Item", {"parent": frappe.session.user}, "count(*)"
-		)
-		self.assertIsNone(frappe.db.exists("Wishlist Item", {"parent": frappe.session.user}))
-		self.assertEqual(wishlist_length, 0)
-
-		# tear down
-		frappe.get_doc("Wishlist", {"user": frappe.session.user}).delete()
-
-	def test_add_remove_in_wishlist_multiple_users(self):
-		"Check if items are added and removed from the correct user's wishlist."
-		test_user = create_user("test_reviewer@example.com", "Customer")
-		test_user_1 = create_user("test_reviewer_1@example.com", "Customer")
-
-		# add to wishlist for first user
-		frappe.set_user(test_user.name)
-		add_to_wishlist("Test Phone Series X")
-
-		# add to wishlist for second user
-		frappe.set_user(test_user_1.name)
-		add_to_wishlist("Test Phone Series X")
-
-		# check wishlist and its content for users
-		self.assertTrue(frappe.db.exists("Wishlist", {"user": test_user.name}))
-		self.assertTrue(
-			frappe.db.exists(
-				"Wishlist Item", {"item_code": "Test Phone Series X", "parent": test_user.name}
-			)
-		)
-
-		self.assertTrue(frappe.db.exists("Wishlist", {"user": test_user_1.name}))
-		self.assertTrue(
-			frappe.db.exists(
-				"Wishlist Item", {"item_code": "Test Phone Series X", "parent": test_user_1.name}
-			)
-		)
-
-		# remove item for second user
-		remove_from_wishlist("Test Phone Series X")
-
-		# make sure item was removed for second user and not first
-		self.assertFalse(
-			frappe.db.exists(
-				"Wishlist Item", {"item_code": "Test Phone Series X", "parent": test_user_1.name}
-			)
-		)
-		self.assertTrue(
-			frappe.db.exists(
-				"Wishlist Item", {"item_code": "Test Phone Series X", "parent": test_user.name}
-			)
-		)
-
-		# remove item for first user
-		frappe.set_user(test_user.name)
-		remove_from_wishlist("Test Phone Series X")
-		self.assertFalse(
-			frappe.db.exists(
-				"Wishlist Item", {"item_code": "Test Phone Series X", "parent": test_user.name}
-			)
-		)
-
-		# tear down
-		frappe.set_user("Administrator")
-		frappe.get_doc("Wishlist", {"user": test_user.name}).delete()
-		frappe.get_doc("Wishlist", {"user": test_user_1.name}).delete()
diff --git a/erpnext/e_commerce/doctype/wishlist/wishlist.js b/erpnext/e_commerce/doctype/wishlist/wishlist.js
deleted file mode 100644
index d96e552..0000000
--- a/erpnext/e_commerce/doctype/wishlist/wishlist.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Wishlist', {
-	// refresh: function(frm) {
-
-	// }
-});
diff --git a/erpnext/e_commerce/doctype/wishlist/wishlist.json b/erpnext/e_commerce/doctype/wishlist/wishlist.json
deleted file mode 100644
index 922924e..0000000
--- a/erpnext/e_commerce/doctype/wishlist/wishlist.json
+++ /dev/null
@@ -1,65 +0,0 @@
-{
- "actions": [],
- "autoname": "field:user",
- "creation": "2021-03-10 18:52:28.769126",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "user",
-  "section_break_2",
-  "items"
- ],
- "fields": [
-  {
-   "fieldname": "user",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "User",
-   "options": "User",
-   "reqd": 1,
-   "unique": 1
-  },
-  {
-   "fieldname": "section_break_2",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fieldname": "items",
-   "fieldtype": "Table",
-   "label": "Items",
-   "options": "Wishlist Item"
-  }
- ],
- "in_create": 1,
- "index_web_pages_for_search": 1,
- "links": [],
- "modified": "2021-07-08 13:11:21.693956",
- "modified_by": "Administrator",
- "module": "E-commerce",
- "name": "Wishlist",
- "owner": "Administrator",
- "permissions": [
-  {
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "System Manager",
-   "share": 1
-  },
-  {
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Website Manager",
-   "share": 1
-  }
- ],
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/e_commerce/doctype/wishlist/wishlist.py b/erpnext/e_commerce/doctype/wishlist/wishlist.py
deleted file mode 100644
index eb74027..0000000
--- a/erpnext/e_commerce/doctype/wishlist/wishlist.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-import frappe
-from frappe.model.document import Document
-
-
-class Wishlist(Document):
-	pass
-
-
-@frappe.whitelist()
-def add_to_wishlist(item_code):
-	"""Insert Item into wishlist."""
-
-	if frappe.db.exists("Wishlist Item", {"item_code": item_code, "parent": frappe.session.user}):
-		return
-
-	web_item_data = frappe.db.get_value(
-		"Website Item",
-		{"item_code": item_code},
-		[
-			"website_image",
-			"website_warehouse",
-			"name",
-			"web_item_name",
-			"item_name",
-			"item_group",
-			"route",
-		],
-		as_dict=1,
-	)
-
-	wished_item_dict = {
-		"item_code": item_code,
-		"item_name": web_item_data.get("item_name"),
-		"item_group": web_item_data.get("item_group"),
-		"website_item": web_item_data.get("name"),
-		"web_item_name": web_item_data.get("web_item_name"),
-		"image": web_item_data.get("website_image"),
-		"warehouse": web_item_data.get("website_warehouse"),
-		"route": web_item_data.get("route"),
-	}
-
-	if not frappe.db.exists("Wishlist", frappe.session.user):
-		# initialise wishlist
-		wishlist = frappe.get_doc({"doctype": "Wishlist"})
-		wishlist.user = frappe.session.user
-		wishlist.append("items", wished_item_dict)
-		wishlist.save(ignore_permissions=True)
-	else:
-		wishlist = frappe.get_doc("Wishlist", frappe.session.user)
-		item = wishlist.append("items", wished_item_dict)
-		item.db_insert()
-
-	if hasattr(frappe.local, "cookie_manager"):
-		frappe.local.cookie_manager.set_cookie("wish_count", str(len(wishlist.items)))
-
-
-@frappe.whitelist()
-def remove_from_wishlist(item_code):
-	if frappe.db.exists("Wishlist Item", {"item_code": item_code, "parent": frappe.session.user}):
-		frappe.db.delete("Wishlist Item", {"item_code": item_code, "parent": frappe.session.user})
-		frappe.db.commit()  # nosemgrep
-
-		wishlist_items = frappe.db.get_values("Wishlist Item", filters={"parent": frappe.session.user})
-
-		if hasattr(frappe.local, "cookie_manager"):
-			frappe.local.cookie_manager.set_cookie("wish_count", str(len(wishlist_items)))
diff --git a/erpnext/e_commerce/doctype/wishlist_item/__init__.py b/erpnext/e_commerce/doctype/wishlist_item/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/e_commerce/doctype/wishlist_item/__init__.py
+++ /dev/null
diff --git a/erpnext/e_commerce/doctype/wishlist_item/wishlist_item.json b/erpnext/e_commerce/doctype/wishlist_item/wishlist_item.json
deleted file mode 100644
index c0414a7..0000000
--- a/erpnext/e_commerce/doctype/wishlist_item/wishlist_item.json
+++ /dev/null
@@ -1,147 +0,0 @@
-{
- "actions": [],
- "creation": "2021-03-10 19:03:00.662714",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "item_code",
-  "website_item",
-  "web_item_name",
-  "column_break_3",
-  "item_name",
-  "item_group",
-  "item_details_section",
-  "description",
-  "column_break_7",
-  "route",
-  "image",
-  "image_view",
-  "section_break_8",
-  "warehouse_section",
-  "warehouse"
- ],
- "fields": [
-  {
-   "fetch_from": "website_item.item_code",
-   "fetch_if_empty": 1,
-   "fieldname": "item_code",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Item Code",
-   "options": "Item",
-   "reqd": 1
-  },
-  {
-   "fieldname": "website_item",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Website Item",
-   "options": "Website Item",
-   "read_only": 1
-  },
-  {
-   "fieldname": "column_break_3",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fetch_from": "item_code.item_name",
-   "fetch_if_empty": 1,
-   "fieldname": "item_name",
-   "fieldtype": "Data",
-   "label": "Item Name",
-   "read_only": 1
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "item_details_section",
-   "fieldtype": "Section Break",
-   "label": "Item Details",
-   "read_only": 1
-  },
-  {
-   "fetch_from": "item_code.description",
-   "fetch_if_empty": 1,
-   "fieldname": "description",
-   "fieldtype": "Text Editor",
-   "label": "Description",
-   "read_only": 1
-  },
-  {
-   "fieldname": "column_break_7",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fetch_from": "item_code.image",
-   "fetch_if_empty": 1,
-   "fieldname": "image",
-   "fieldtype": "Attach",
-   "hidden": 1,
-   "label": "Image"
-  },
-  {
-   "fetch_from": "item_code.image",
-   "fetch_if_empty": 1,
-   "fieldname": "image_view",
-   "fieldtype": "Image",
-   "hidden": 1,
-   "label": "Image View",
-   "options": "image",
-   "print_hide": 1
-  },
-  {
-   "fieldname": "warehouse_section",
-   "fieldtype": "Section Break",
-   "label": "Warehouse"
-  },
-  {
-   "fieldname": "warehouse",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Warehouse",
-   "options": "Warehouse",
-   "read_only": 1
-  },
-  {
-   "fieldname": "section_break_8",
-   "fieldtype": "Section Break"
-  },
-  {
-   "fetch_from": "item_code.item_group",
-   "fetch_if_empty": 1,
-   "fieldname": "item_group",
-   "fieldtype": "Link",
-   "label": "Item Group",
-   "options": "Item Group",
-   "read_only": 1
-  },
-  {
-   "fetch_from": "website_item.route",
-   "fetch_if_empty": 1,
-   "fieldname": "route",
-   "fieldtype": "Small Text",
-   "label": "Route",
-   "read_only": 1
-  },
-  {
-   "fetch_from": "website_item.web_item_name",
-   "fetch_if_empty": 1,
-   "fieldname": "web_item_name",
-   "fieldtype": "Data",
-   "label": "Website Item Name",
-   "read_only": 1
-  }
- ],
- "index_web_pages_for_search": 1,
- "istable": 1,
- "links": [],
- "modified": "2021-08-09 10:30:41.964802",
- "modified_by": "Administrator",
- "module": "E-commerce",
- "name": "Wishlist Item",
- "owner": "Administrator",
- "permissions": [],
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/e_commerce/doctype/wishlist_item/wishlist_item.py b/erpnext/e_commerce/doctype/wishlist_item/wishlist_item.py
deleted file mode 100644
index 75ebccb..0000000
--- a/erpnext/e_commerce/doctype/wishlist_item/wishlist_item.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-# import frappe
-from frappe.model.document import Document
-
-
-class WishlistItem(Document):
-	pass
diff --git a/erpnext/e_commerce/legacy_search.py b/erpnext/e_commerce/legacy_search.py
deleted file mode 100644
index ef8e86d..0000000
--- a/erpnext/e_commerce/legacy_search.py
+++ /dev/null
@@ -1,134 +0,0 @@
-import frappe
-from frappe.search.full_text_search import FullTextSearch
-from frappe.utils import strip_html_tags
-from whoosh.analysis import StemmingAnalyzer
-from whoosh.fields import ID, KEYWORD, TEXT, Schema
-from whoosh.qparser import FieldsPlugin, MultifieldParser, WildcardPlugin
-from whoosh.query import Prefix
-
-# TODO: Make obsolete
-INDEX_NAME = "products"
-
-
-class ProductSearch(FullTextSearch):
-	"""Wrapper for WebsiteSearch"""
-
-	def get_schema(self):
-		return Schema(
-			title=TEXT(stored=True, field_boost=1.5),
-			name=ID(stored=True),
-			path=ID(stored=True),
-			content=TEXT(stored=True, analyzer=StemmingAnalyzer()),
-			keywords=KEYWORD(stored=True, scorable=True, commas=True),
-		)
-
-	def get_id(self):
-		return "name"
-
-	def get_items_to_index(self):
-		"""Get all routes to be indexed, this includes the static pages
-		in www/ and routes from published documents
-
-		Returns:
-		        self (object): FullTextSearch Instance
-		"""
-		items = get_all_published_items()
-		documents = [self.get_document_to_index(item) for item in items]
-		return documents
-
-	def get_document_to_index(self, item):
-		try:
-			item = frappe.get_doc("Item", item)
-			title = item.item_name
-			keywords = [item.item_group]
-
-			if item.brand:
-				keywords.append(item.brand)
-
-			if item.website_image_alt:
-				keywords.append(item.website_image_alt)
-
-			if item.has_variants and item.variant_based_on == "Item Attribute":
-				keywords = keywords + [attr.attribute for attr in item.attributes]
-
-			if item.web_long_description:
-				content = strip_html_tags(item.web_long_description)
-			elif item.description:
-				content = strip_html_tags(item.description)
-
-			return frappe._dict(
-				title=title,
-				name=item.name,
-				path=item.route,
-				content=content,
-				keywords=", ".join(keywords),
-			)
-		except Exception:
-			pass
-
-	def search(self, text, scope=None, limit=20):
-		"""Search from the current index
-
-		Args:
-		        text (str): String to search for
-		        scope (str, optional): Scope to limit the search. Defaults to None.
-		        limit (int, optional): Limit number of search results. Defaults to 20.
-
-		Returns:
-		        [List(_dict)]: Search results
-		"""
-		ix = self.get_index()
-
-		results = None
-		out = []
-
-		with ix.searcher() as searcher:
-			parser = MultifieldParser(["title", "content", "keywords"], ix.schema)
-			parser.remove_plugin_class(FieldsPlugin)
-			parser.remove_plugin_class(WildcardPlugin)
-			query = parser.parse(text)
-
-			filter_scoped = None
-			if scope:
-				filter_scoped = Prefix(self.id, scope)
-			results = searcher.search(query, limit=limit, filter=filter_scoped)
-
-			for r in results:
-				out.append(self.parse_result(r))
-
-		return out
-
-	def parse_result(self, result):
-		title_highlights = result.highlights("title")
-		content_highlights = result.highlights("content")
-		keyword_highlights = result.highlights("keywords")
-
-		return frappe._dict(
-			title=result["title"],
-			path=result["path"],
-			keywords=result["keywords"],
-			title_highlights=title_highlights,
-			content_highlights=content_highlights,
-			keyword_highlights=keyword_highlights,
-		)
-
-
-def get_all_published_items():
-	return frappe.get_all(
-		"Website Item", filters={"variant_of": "", "published": 1}, pluck="item_code"
-	)
-
-
-def update_index_for_path(path):
-	search = ProductSearch(INDEX_NAME)
-	return search.update_index_by_name(path)
-
-
-def remove_document_from_index(path):
-	search = ProductSearch(INDEX_NAME)
-	return search.remove_document_from_index(path)
-
-
-def build_index_for_all_routes():
-	search = ProductSearch(INDEX_NAME)
-	return search.build()
diff --git a/erpnext/e_commerce/product_data_engine/filters.py b/erpnext/e_commerce/product_data_engine/filters.py
deleted file mode 100644
index e5e5e97..0000000
--- a/erpnext/e_commerce/product_data_engine/filters.py
+++ /dev/null
@@ -1,158 +0,0 @@
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-import frappe
-from frappe.utils import floor
-
-
-class ProductFiltersBuilder:
-	def __init__(self, item_group=None):
-		if not item_group:
-			self.doc = frappe.get_doc("E Commerce Settings")
-		else:
-			self.doc = frappe.get_doc("Item Group", item_group)
-
-		self.item_group = item_group
-
-	def get_field_filters(self):
-		from erpnext.setup.doctype.item_group.item_group import get_child_groups_for_website
-
-		if not self.item_group and not self.doc.enable_field_filters:
-			return
-
-		fields, filter_data = [], []
-		filter_fields = [row.fieldname for row in self.doc.filter_fields]  # fields in settings
-
-		# filter valid field filters i.e. those that exist in Website Item
-		web_item_meta = frappe.get_meta("Website Item", cached=True)
-		fields = [
-			web_item_meta.get_field(field) for field in filter_fields if web_item_meta.has_field(field)
-		]
-
-		for df in fields:
-			item_filters, item_or_filters = {"published": 1}, []
-			link_doctype_values = self.get_filtered_link_doctype_records(df)
-
-			if df.fieldtype == "Link":
-				if self.item_group:
-					include_child = frappe.db.get_value("Item Group", self.item_group, "include_descendants")
-					if include_child:
-						include_groups = get_child_groups_for_website(self.item_group, include_self=True)
-						include_groups = [x.name for x in include_groups]
-						item_or_filters.extend(
-							[
-								["item_group", "in", include_groups],
-								["Website Item Group", "item_group", "=", self.item_group],  # consider website item groups
-							]
-						)
-					else:
-						item_or_filters.extend(
-							[
-								["item_group", "=", self.item_group],
-								["Website Item Group", "item_group", "=", self.item_group],  # consider website item groups
-							]
-						)
-
-				# exclude variants if mentioned in settings
-				if frappe.db.get_single_value("E Commerce Settings", "hide_variants"):
-					item_filters["variant_of"] = ["is", "not set"]
-
-				# Get link field values attached to published items
-				item_values = frappe.get_all(
-					"Website Item",
-					fields=[df.fieldname],
-					filters=item_filters,
-					or_filters=item_or_filters,
-					distinct="True",
-					pluck=df.fieldname,
-				)
-
-				values = list(set(item_values) & link_doctype_values)  # intersection of both
-			else:
-				# table multiselect
-				values = list(link_doctype_values)
-
-			# Remove None
-			if None in values:
-				values.remove(None)
-
-			if values:
-				filter_data.append([df, values])
-
-		return filter_data
-
-	def get_filtered_link_doctype_records(self, field):
-		"""
-		Get valid link doctype records depending on filters.
-		Apply enable/disable/show_in_website filter.
-		Returns:
-		        set: A set containing valid record names
-		"""
-		link_doctype = field.get_link_doctype()
-		meta = frappe.get_meta(link_doctype, cached=True) if link_doctype else None
-		if meta:
-			filters = self.get_link_doctype_filters(meta)
-			link_doctype_values = set(d.name for d in frappe.get_all(link_doctype, filters))
-
-		return link_doctype_values if meta else set()
-
-	def get_link_doctype_filters(self, meta):
-		"Filters for Link Doctype eg. 'show_in_website'."
-		filters = {}
-		if not meta:
-			return filters
-
-		if meta.has_field("enabled"):
-			filters["enabled"] = 1
-		if meta.has_field("disabled"):
-			filters["disabled"] = 0
-		if meta.has_field("show_in_website"):
-			filters["show_in_website"] = 1
-
-		return filters
-
-	def get_attribute_filters(self):
-		if not self.item_group and not self.doc.enable_attribute_filters:
-			return
-
-		attributes = [row.attribute for row in self.doc.filter_attributes]
-
-		if not attributes:
-			return []
-
-		result = frappe.get_all(
-			"Item Variant Attribute",
-			filters={"attribute": ["in", attributes], "attribute_value": ["is", "set"]},
-			fields=["attribute", "attribute_value"],
-			distinct=True,
-		)
-
-		attribute_value_map = {}
-		for d in result:
-			attribute_value_map.setdefault(d.attribute, []).append(d.attribute_value)
-
-		out = []
-		for name, values in attribute_value_map.items():
-			out.append(frappe._dict(name=name, item_attribute_values=values))
-		return out
-
-	def get_discount_filters(self, discounts):
-		discount_filters = []
-
-		# [25.89, 60.5] min max
-		min_discount, max_discount = discounts[0], discounts[1]
-		# [25, 60] rounded min max
-		min_range_absolute, max_range_absolute = floor(min_discount), floor(max_discount)
-
-		min_range = int(min_discount - (min_range_absolute % 10))  # 20
-		max_range = int(max_discount - (max_range_absolute % 10))  # 60
-
-		min_range = (
-			(min_range + 10) if min_range != min_range_absolute else min_range
-		)  # 30 (upper limit of 25.89 in range of 10)
-		max_range = (max_range + 10) if max_range != max_range_absolute else max_range  # 60
-
-		for discount in range(min_range, (max_range + 1), 10):
-			label = f"{discount}% and below"
-			discount_filters.append([discount, label])
-
-		return discount_filters
diff --git a/erpnext/e_commerce/product_data_engine/query.py b/erpnext/e_commerce/product_data_engine/query.py
deleted file mode 100644
index 975f876..0000000
--- a/erpnext/e_commerce/product_data_engine/query.py
+++ /dev/null
@@ -1,321 +0,0 @@
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-from frappe.utils import flt
-
-from erpnext.e_commerce.doctype.item_review.item_review import get_customer
-from erpnext.e_commerce.shopping_cart.product_info import get_product_info_for_website
-from erpnext.utilities.product import get_non_stock_item_status
-
-
-class ProductQuery:
-	"""Query engine for product listing
-
-	Attributes:
-	        fields (list): Fields to fetch in query
-	        conditions (string): Conditions for query building
-	        or_conditions (string): Search conditions
-	        page_length (Int): Length of page for the query
-	        settings (Document): E Commerce Settings DocType
-	"""
-
-	def __init__(self):
-		self.settings = frappe.get_doc("E Commerce Settings")
-		self.page_length = self.settings.products_per_page or 20
-
-		self.or_filters = []
-		self.filters = [["published", "=", 1]]
-		self.fields = [
-			"web_item_name",
-			"name",
-			"item_name",
-			"item_code",
-			"website_image",
-			"variant_of",
-			"has_variants",
-			"item_group",
-			"web_long_description",
-			"short_description",
-			"route",
-			"website_warehouse",
-			"ranking",
-			"on_backorder",
-		]
-
-	def query(self, attributes=None, fields=None, search_term=None, start=0, item_group=None):
-		"""
-		Args:
-		        attributes (dict, optional): Item Attribute filters
-		        fields (dict, optional): Field level filters
-		        search_term (str, optional): Search term to lookup
-		        start (int, optional): Page start
-
-		Returns:
-		        dict: Dict containing items, item count & discount range
-		"""
-		# track if discounts included in field filters
-		self.filter_with_discount = bool(fields.get("discount"))
-		result, discount_list, website_item_groups, cart_items, count = [], [], [], [], 0
-
-		if fields:
-			self.build_fields_filters(fields)
-		if item_group:
-			self.build_item_group_filters(item_group)
-		if search_term:
-			self.build_search_filters(search_term)
-		if self.settings.hide_variants:
-			self.filters.append(["variant_of", "is", "not set"])
-
-		# query results
-		if attributes:
-			result, count = self.query_items_with_attributes(attributes, start)
-		else:
-			result, count = self.query_items(start=start)
-
-		# sort combined results by ranking
-		result = sorted(result, key=lambda x: x.get("ranking"), reverse=True)
-
-		if self.settings.enabled:
-			cart_items = self.get_cart_items()
-
-		result, discount_list = self.add_display_details(result, discount_list, cart_items)
-
-		discounts = []
-		if discount_list:
-			discounts = [min(discount_list), max(discount_list)]
-
-		result = self.filter_results_by_discount(fields, result)
-
-		return {"items": result, "items_count": count, "discounts": discounts}
-
-	def query_items(self, start=0):
-		"""Build a query to fetch Website Items based on field filters."""
-		# MySQL does not support offset without limit,
-		# frappe does not accept two parameters for limit
-		# https://dev.mysql.com/doc/refman/8.0/en/select.html#id4651989
-		count_items = frappe.db.get_all(
-			"Website Item",
-			filters=self.filters,
-			or_filters=self.or_filters,
-			limit_page_length=184467440737095516,
-			limit_start=start,  # get all items from this offset for total count ahead
-			order_by="ranking desc",
-		)
-		count = len(count_items)
-
-		# If discounts included, return all rows.
-		# Slice after filtering rows with discount (See `filter_results_by_discount`).
-		# Slicing before hand will miss discounted items on the 3rd or 4th page.
-		# Discounts are fetched on computing Pricing Rules so we cannot query them directly.
-		page_length = 184467440737095516 if self.filter_with_discount else self.page_length
-
-		items = frappe.db.get_all(
-			"Website Item",
-			fields=self.fields,
-			filters=self.filters,
-			or_filters=self.or_filters,
-			limit_page_length=page_length,
-			limit_start=start,
-			order_by="ranking desc",
-		)
-
-		return items, count
-
-	def query_items_with_attributes(self, attributes, start=0):
-		"""Build a query to fetch Website Items based on field & attribute filters."""
-		item_codes = []
-
-		for attribute, values in attributes.items():
-			if not isinstance(values, list):
-				values = [values]
-
-			# get items that have selected attribute & value
-			item_code_list = frappe.db.get_all(
-				"Item",
-				fields=["item_code"],
-				filters=[
-					["published_in_website", "=", 1],
-					["Item Variant Attribute", "attribute", "=", attribute],
-					["Item Variant Attribute", "attribute_value", "in", values],
-				],
-			)
-			item_codes.append({x.item_code for x in item_code_list})
-
-		if item_codes:
-			item_codes = list(set.intersection(*item_codes))
-			self.filters.append(["item_code", "in", item_codes])
-
-		items, count = self.query_items(start=start)
-
-		return items, count
-
-	def build_fields_filters(self, filters):
-		"""Build filters for field values
-
-		Args:
-		        filters (dict): Filters
-		"""
-		for field, values in filters.items():
-			if not values or field == "discount":
-				continue
-
-			# handle multiselect fields in filter addition
-			meta = frappe.get_meta("Website Item", cached=True)
-			df = meta.get_field(field)
-			if df.fieldtype == "Table MultiSelect":
-				child_doctype = df.options
-				child_meta = frappe.get_meta(child_doctype, cached=True)
-				fields = child_meta.get("fields")
-				if fields:
-					self.filters.append([child_doctype, fields[0].fieldname, "IN", values])
-			elif isinstance(values, list):
-				# If value is a list use `IN` query
-				self.filters.append([field, "in", values])
-			else:
-				# `=` will be faster than `IN` for most cases
-				self.filters.append([field, "=", values])
-
-	def build_item_group_filters(self, item_group):
-		"Add filters for Item group page and include Website Item Groups."
-		from erpnext.setup.doctype.item_group.item_group import get_child_groups_for_website
-
-		item_group_filters = []
-
-		item_group_filters.append(["Website Item", "item_group", "=", item_group])
-		# Consider Website Item Groups
-		item_group_filters.append(["Website Item Group", "item_group", "=", item_group])
-
-		if frappe.db.get_value("Item Group", item_group, "include_descendants"):
-			# include child item group's items as well
-			# eg. Group Node A, will show items of child 1 and child 2 as well
-			# on it's web page
-			include_groups = get_child_groups_for_website(item_group, include_self=True)
-			include_groups = [x.name for x in include_groups]
-			item_group_filters.append(["Website Item", "item_group", "in", include_groups])
-
-		self.or_filters.extend(item_group_filters)
-
-	def build_search_filters(self, search_term):
-		"""Query search term in specified fields
-
-		Args:
-		        search_term (str): Search candidate
-		"""
-		# Default fields to search from
-		default_fields = {"item_code", "item_name", "web_long_description", "item_group"}
-
-		# Get meta search fields
-		meta = frappe.get_meta("Website Item")
-		meta_fields = set(meta.get_search_fields())
-
-		# Join the meta fields and default fields set
-		search_fields = default_fields.union(meta_fields)
-		if frappe.db.count("Website Item", cache=True) > 50000:
-			search_fields.discard("web_long_description")
-
-		# Build or filters for query
-		search = "%{}%".format(search_term)
-		for field in search_fields:
-			self.or_filters.append([field, "like", search])
-
-	def add_display_details(self, result, discount_list, cart_items):
-		"""Add price and availability details in result."""
-		for item in result:
-			product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get(
-				"product_info"
-			)
-
-			if product_info and product_info["price"]:
-				# update/mutate item and discount_list objects
-				self.get_price_discount_info(item, product_info["price"], discount_list)
-
-			if self.settings.show_stock_availability:
-				self.get_stock_availability(item)
-
-			item.in_cart = item.item_code in cart_items
-
-			item.wished = False
-			if frappe.db.exists(
-				"Wishlist Item", {"item_code": item.item_code, "parent": frappe.session.user}
-			):
-				item.wished = True
-
-		return result, discount_list
-
-	def get_price_discount_info(self, item, price_object, discount_list):
-		"""Modify item object and add price details."""
-		fields = ["formatted_mrp", "formatted_price", "price_list_rate"]
-		for field in fields:
-			item[field] = price_object.get(field)
-
-		if price_object.get("discount_percent"):
-			item.discount_percent = flt(price_object.discount_percent)
-			discount_list.append(price_object.discount_percent)
-
-		if item.formatted_mrp:
-			item.discount = price_object.get("formatted_discount_percent") or price_object.get(
-				"formatted_discount_rate"
-			)
-
-	def get_stock_availability(self, item):
-		from erpnext.templates.pages.wishlist import (
-			get_stock_availability as get_stock_availability_from_template,
-		)
-
-		"""Modify item object and add stock details."""
-		item.in_stock = False
-		warehouse = item.get("website_warehouse")
-		is_stock_item = frappe.get_cached_value("Item", item.item_code, "is_stock_item")
-
-		if item.get("on_backorder"):
-			return
-
-		if not is_stock_item:
-			if warehouse:
-				# product bundle case
-				item.in_stock = get_non_stock_item_status(item.item_code, "website_warehouse")
-			else:
-				item.in_stock = True
-		elif warehouse:
-			item.in_stock = get_stock_availability_from_template(item.item_code, warehouse)
-
-	def get_cart_items(self):
-		customer = get_customer(silent=True)
-		if customer:
-			quotation = frappe.get_all(
-				"Quotation",
-				fields=["name"],
-				filters={
-					"party_name": customer,
-					"contact_email": frappe.session.user,
-					"order_type": "Shopping Cart",
-					"docstatus": 0,
-				},
-				order_by="modified desc",
-				limit_page_length=1,
-			)
-			if quotation:
-				items = frappe.get_all(
-					"Quotation Item", fields=["item_code"], filters={"parent": quotation[0].get("name")}
-				)
-				items = [row.item_code for row in items]
-				return items
-
-		return []
-
-	def filter_results_by_discount(self, fields, result):
-		if fields and fields.get("discount"):
-			discount_percent = frappe.utils.flt(fields["discount"][0])
-			result = [
-				row
-				for row in result
-				if row.get("discount_percent") and row.discount_percent <= discount_percent
-			]
-
-		if self.filter_with_discount:
-			# no limit was added to results while querying
-			# slice results manually
-			result[: self.page_length]
-
-		return result
diff --git a/erpnext/e_commerce/product_data_engine/test_item_group_product_data_engine.py b/erpnext/e_commerce/product_data_engine/test_item_group_product_data_engine.py
deleted file mode 100644
index 45bc20e..0000000
--- a/erpnext/e_commerce/product_data_engine/test_item_group_product_data_engine.py
+++ /dev/null
@@ -1,170 +0,0 @@
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-import unittest
-
-import frappe
-
-from erpnext.e_commerce.api import get_product_filter_data
-from erpnext.e_commerce.doctype.website_item.test_website_item import create_regular_web_item
-
-test_dependencies = ["Item", "Item Group"]
-
-
-class TestItemGroupProductDataEngine(unittest.TestCase):
-	"Test Products & Sub-Category Querying for Product Listing on Item Group Page."
-
-	def setUp(self):
-		item_codes = [
-			("Test Mobile A", "_Test Item Group B"),
-			("Test Mobile B", "_Test Item Group B"),
-			("Test Mobile C", "_Test Item Group B - 1"),
-			("Test Mobile D", "_Test Item Group B - 1"),
-			("Test Mobile E", "_Test Item Group B - 2"),
-		]
-		for item in item_codes:
-			item_code = item[0]
-			item_args = {"item_group": item[1]}
-			if not frappe.db.exists("Website Item", {"item_code": item_code}):
-				create_regular_web_item(item_code, item_args=item_args)
-
-		frappe.db.set_value("Item Group", "_Test Item Group B - 1", "show_in_website", 1)
-		frappe.db.set_value("Item Group", "_Test Item Group B - 2", "show_in_website", 1)
-
-	def tearDown(self):
-		frappe.db.rollback()
-
-	def test_product_listing_in_item_group(self):
-		"Test if only products belonging to the Item Group are fetched."
-		result = get_product_filter_data(
-			query_args={
-				"field_filters": {},
-				"attribute_filters": {},
-				"start": 0,
-				"item_group": "_Test Item Group B",
-			}
-		)
-
-		items = result.get("items")
-		item_codes = [item.get("item_code") for item in items]
-
-		self.assertEqual(len(items), 2)
-		self.assertIn("Test Mobile A", item_codes)
-		self.assertNotIn("Test Mobile C", item_codes)
-
-	def test_products_in_multiple_item_groups(self):
-		"""Test if product is visible on multiple item group pages barring its own."""
-		website_item = frappe.get_doc("Website Item", {"item_code": "Test Mobile E"})
-
-		# show item belonging to '_Test Item Group B - 2' in '_Test Item Group B - 1' as well
-		website_item.append("website_item_groups", {"item_group": "_Test Item Group B - 1"})
-		website_item.save()
-
-		result = get_product_filter_data(
-			query_args={
-				"field_filters": {},
-				"attribute_filters": {},
-				"start": 0,
-				"item_group": "_Test Item Group B - 1",
-			}
-		)
-
-		items = result.get("items")
-		item_codes = [item.get("item_code") for item in items]
-
-		self.assertEqual(len(items), 3)
-		self.assertIn("Test Mobile E", item_codes)  # visible in other item groups
-		self.assertIn("Test Mobile C", item_codes)
-		self.assertIn("Test Mobile D", item_codes)
-
-		result = get_product_filter_data(
-			query_args={
-				"field_filters": {},
-				"attribute_filters": {},
-				"start": 0,
-				"item_group": "_Test Item Group B - 2",
-			}
-		)
-
-		items = result.get("items")
-
-		self.assertEqual(len(items), 1)
-		self.assertEqual(items[0].get("item_code"), "Test Mobile E")  # visible in own item group
-
-	def test_item_group_with_sub_groups(self):
-		"Test Valid Sub Item Groups in Item Group Page."
-		frappe.db.set_value("Item Group", "_Test Item Group B - 2", "show_in_website", 0)
-
-		result = get_product_filter_data(
-			query_args={
-				"field_filters": {},
-				"attribute_filters": {},
-				"start": 0,
-				"item_group": "_Test Item Group B",
-			}
-		)
-
-		self.assertTrue(bool(result.get("sub_categories")))
-
-		child_groups = [d.name for d in result.get("sub_categories")]
-		# check if child group is fetched if shown in website
-		self.assertIn("_Test Item Group B - 1", child_groups)
-
-		frappe.db.set_value("Item Group", "_Test Item Group B - 2", "show_in_website", 1)
-		result = get_product_filter_data(
-			query_args={
-				"field_filters": {},
-				"attribute_filters": {},
-				"start": 0,
-				"item_group": "_Test Item Group B",
-			}
-		)
-		child_groups = [d.name for d in result.get("sub_categories")]
-
-		# check if child group is fetched if shown in website
-		self.assertIn("_Test Item Group B - 1", child_groups)
-		self.assertIn("_Test Item Group B - 2", child_groups)
-
-	def test_item_group_page_with_descendants_included(self):
-		"""
-		Test if 'include_descendants' pulls Items belonging to descendant Item Groups (Level 2 & 3).
-		> _Test Item Group B [Level 1]
-		        > _Test Item Group B - 1 [Level 2]
-		                > _Test Item Group B - 1 - 1 [Level 3]
-		"""
-		frappe.get_doc(
-			{  # create Level 3 nested child group
-				"doctype": "Item Group",
-				"is_group": 1,
-				"item_group_name": "_Test Item Group B - 1 - 1",
-				"parent_item_group": "_Test Item Group B - 1",
-			}
-		).insert()
-
-		create_regular_web_item(  # create an item belonging to level 3 item group
-			"Test Mobile F", item_args={"item_group": "_Test Item Group B - 1 - 1"}
-		)
-
-		frappe.db.set_value("Item Group", "_Test Item Group B - 1 - 1", "show_in_website", 1)
-
-		# enable 'include descendants' in Level 1
-		frappe.db.set_value("Item Group", "_Test Item Group B", "include_descendants", 1)
-
-		result = get_product_filter_data(
-			query_args={
-				"field_filters": {},
-				"attribute_filters": {},
-				"start": 0,
-				"item_group": "_Test Item Group B",
-			}
-		)
-
-		items = result.get("items")
-		item_codes = [item.get("item_code") for item in items]
-
-		# check if all sub groups' items are pulled
-		self.assertEqual(len(items), 6)
-		self.assertIn("Test Mobile A", item_codes)
-		self.assertIn("Test Mobile C", item_codes)
-		self.assertIn("Test Mobile E", item_codes)
-		self.assertIn("Test Mobile F", item_codes)
diff --git a/erpnext/e_commerce/product_data_engine/test_product_data_engine.py b/erpnext/e_commerce/product_data_engine/test_product_data_engine.py
deleted file mode 100644
index c3b6ed5..0000000
--- a/erpnext/e_commerce/product_data_engine/test_product_data_engine.py
+++ /dev/null
@@ -1,348 +0,0 @@
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-import unittest
-
-import frappe
-
-from erpnext.e_commerce.doctype.e_commerce_settings.test_e_commerce_settings import (
-	setup_e_commerce_settings,
-)
-from erpnext.e_commerce.doctype.website_item.test_website_item import create_regular_web_item
-from erpnext.e_commerce.product_data_engine.filters import ProductFiltersBuilder
-from erpnext.e_commerce.product_data_engine.query import ProductQuery
-
-test_dependencies = ["Item", "Item Group"]
-
-
-class TestProductDataEngine(unittest.TestCase):
-	"Test Products Querying and Filters for Product Listing."
-
-	@classmethod
-	def setUpClass(cls):
-		item_codes = [
-			("Test 11I Laptop", "Products"),  # rank 1
-			("Test 12I Laptop", "Products"),  # rank 2
-			("Test 13I Laptop", "Products"),  # rank 3
-			("Test 14I Laptop", "Raw Material"),  # rank 4
-			("Test 15I Laptop", "Raw Material"),  # rank 5
-			("Test 16I Laptop", "Raw Material"),  # rank 6
-			("Test 17I Laptop", "Products"),  # rank 7
-		]
-		for index, item in enumerate(item_codes, start=1):
-			item_code = item[0]
-			item_args = {"item_group": item[1]}
-			web_args = {"ranking": index}
-			if not frappe.db.exists("Website Item", {"item_code": item_code}):
-				create_regular_web_item(item_code, item_args=item_args, web_args=web_args)
-
-		setup_e_commerce_settings(
-			{
-				"products_per_page": 4,
-				"enable_field_filters": 1,
-				"filter_fields": [{"fieldname": "item_group"}],
-				"enable_attribute_filters": 1,
-				"filter_attributes": [{"attribute": "Test Size"}],
-				"company": "_Test Company",
-				"enabled": 1,
-				"default_customer_group": "_Test Customer Group",
-				"price_list": "_Test Price List India",
-			}
-		)
-		frappe.local.shopping_cart_settings = None
-
-	@classmethod
-	def tearDownClass(cls):
-		frappe.db.rollback()
-
-	def test_product_list_ordering_and_paging(self):
-		"Test if website items appear by ranking on different pages."
-		engine = ProductQuery()
-		result = engine.query(attributes={}, fields={}, search_term=None, start=0, item_group=None)
-		items = result.get("items")
-
-		self.assertIsNotNone(items)
-		self.assertEqual(len(items), 4)
-		self.assertGreater(result.get("items_count"), 4)
-
-		# check if items appear as per ranking set in setUpClass
-		self.assertEqual(items[0].get("item_code"), "Test 17I Laptop")
-		self.assertEqual(items[1].get("item_code"), "Test 16I Laptop")
-		self.assertEqual(items[2].get("item_code"), "Test 15I Laptop")
-		self.assertEqual(items[3].get("item_code"), "Test 14I Laptop")
-
-		# check next page
-		result = engine.query(attributes={}, fields={}, search_term=None, start=4, item_group=None)
-		items = result.get("items")
-
-		# check if items appear as per ranking set in setUpClass on next page
-		self.assertEqual(items[0].get("item_code"), "Test 13I Laptop")
-		self.assertEqual(items[1].get("item_code"), "Test 12I Laptop")
-		self.assertEqual(items[2].get("item_code"), "Test 11I Laptop")
-
-	def test_change_product_ranking(self):
-		"Test if item on second page appear on first if ranking is changed."
-		item_code = "Test 12I Laptop"
-		old_ranking = frappe.db.get_value("Website Item", {"item_code": item_code}, "ranking")
-
-		# low rank, appears on second page
-		self.assertEqual(old_ranking, 2)
-
-		# set ranking as highest rank
-		frappe.db.set_value("Website Item", {"item_code": item_code}, "ranking", 10)
-
-		engine = ProductQuery()
-		result = engine.query(attributes={}, fields={}, search_term=None, start=0, item_group=None)
-		items = result.get("items")
-
-		# check if item is the first item on the first page
-		self.assertEqual(items[0].get("item_code"), item_code)
-		self.assertEqual(items[1].get("item_code"), "Test 17I Laptop")
-
-		# tear down
-		frappe.db.set_value("Website Item", {"item_code": item_code}, "ranking", old_ranking)
-
-	def test_product_list_field_filter_builder(self):
-		"Test if field filters are fetched correctly."
-		frappe.db.set_value("Item Group", "Raw Material", "show_in_website", 0)
-
-		filter_engine = ProductFiltersBuilder()
-		field_filters = filter_engine.get_field_filters()
-
-		# Web Items belonging to 'Products' and 'Raw Material' are available
-		# but only 'Products' has 'show_in_website' enabled
-		item_group_filters = field_filters[0]
-		docfield = item_group_filters[0]
-		valid_item_groups = item_group_filters[1]
-
-		self.assertEqual(docfield.options, "Item Group")
-		self.assertIn("Products", valid_item_groups)
-		self.assertNotIn("Raw Material", valid_item_groups)
-
-		frappe.db.set_value("Item Group", "Raw Material", "show_in_website", 1)
-		field_filters = filter_engine.get_field_filters()
-
-		#'Products' and 'Raw Materials' both have 'show_in_website' enabled
-		item_group_filters = field_filters[0]
-		docfield = item_group_filters[0]
-		valid_item_groups = item_group_filters[1]
-
-		self.assertEqual(docfield.options, "Item Group")
-		self.assertIn("Products", valid_item_groups)
-		self.assertIn("Raw Material", valid_item_groups)
-
-	def test_product_list_with_field_filter(self):
-		"Test if field filters are applied correctly."
-		field_filters = {"item_group": "Raw Material"}
-
-		engine = ProductQuery()
-		result = engine.query(
-			attributes={}, fields=field_filters, search_term=None, start=0, item_group=None
-		)
-		items = result.get("items")
-
-		# check if only 'Raw Material' are fetched in the right order
-		self.assertEqual(len(items), 3)
-		self.assertEqual(items[0].get("item_code"), "Test 16I Laptop")
-		self.assertEqual(items[1].get("item_code"), "Test 15I Laptop")
-
-	# def test_product_list_with_field_filter_table_multiselect(self):
-	# 	TODO
-	# 	pass
-
-	def test_product_list_attribute_filter_builder(self):
-		"Test if attribute filters are fetched correctly."
-		create_variant_web_item()
-
-		filter_engine = ProductFiltersBuilder()
-		attribute_filter = filter_engine.get_attribute_filters()[0]
-		attribute_values = attribute_filter.item_attribute_values
-
-		self.assertEqual(attribute_filter.name, "Test Size")
-		self.assertGreater(len(attribute_values), 0)
-		self.assertIn("Large", attribute_values)
-
-	def test_product_list_with_attribute_filter(self):
-		"Test if attribute filters are applied correctly."
-		create_variant_web_item()
-
-		attribute_filters = {"Test Size": ["Large"]}
-		engine = ProductQuery()
-		result = engine.query(
-			attributes=attribute_filters, fields={}, search_term=None, start=0, item_group=None
-		)
-		items = result.get("items")
-
-		# check if only items with Test Size 'Large' are fetched
-		self.assertEqual(len(items), 1)
-		self.assertEqual(items[0].get("item_code"), "Test Web Item-L")
-
-	def test_product_list_discount_filter_builder(self):
-		"Test if discount filters are fetched correctly."
-		from erpnext.e_commerce.doctype.website_item.test_website_item import (
-			make_web_item_price,
-			make_web_pricing_rule,
-		)
-
-		item_code = "Test 12I Laptop"
-		make_web_item_price(item_code=item_code)
-		make_web_pricing_rule(title=f"Test Pricing Rule for {item_code}", item_code=item_code, selling=1)
-
-		setup_e_commerce_settings({"show_price": 1})
-		frappe.local.shopping_cart_settings = None
-
-		engine = ProductQuery()
-		result = engine.query(attributes={}, fields={}, search_term=None, start=4, item_group=None)
-		self.assertTrue(bool(result.get("discounts")))
-
-		filter_engine = ProductFiltersBuilder()
-		discount_filters = filter_engine.get_discount_filters(result["discounts"])
-
-		self.assertEqual(len(discount_filters[0]), 2)
-		self.assertEqual(discount_filters[0][0], 10)
-		self.assertEqual(discount_filters[0][1], "10% and below")
-
-	def test_product_list_with_discount_filters(self):
-		"Test if discount filters are applied correctly."
-		from erpnext.e_commerce.doctype.website_item.test_website_item import (
-			make_web_item_price,
-			make_web_pricing_rule,
-		)
-
-		field_filters = {"discount": [10]}
-
-		make_web_item_price(item_code="Test 12I Laptop")
-		make_web_pricing_rule(
-			title="Test Pricing Rule for Test 12I Laptop",  # 10% discount
-			item_code="Test 12I Laptop",
-			selling=1,
-		)
-		make_web_item_price(item_code="Test 13I Laptop")
-		make_web_pricing_rule(
-			title="Test Pricing Rule for Test 13I Laptop",  # 15% discount
-			item_code="Test 13I Laptop",
-			discount_percentage=15,
-			selling=1,
-		)
-
-		setup_e_commerce_settings({"show_price": 1})
-		frappe.local.shopping_cart_settings = None
-
-		engine = ProductQuery()
-		result = engine.query(
-			attributes={}, fields=field_filters, search_term=None, start=0, item_group=None
-		)
-		items = result.get("items")
-
-		# check if only product with 10% and below discount are fetched
-		self.assertEqual(len(items), 1)
-		self.assertEqual(items[0].get("item_code"), "Test 12I Laptop")
-
-	def test_product_list_with_api(self):
-		"Test products listing using API."
-		from erpnext.e_commerce.api import get_product_filter_data
-
-		create_variant_web_item()
-
-		result = get_product_filter_data(
-			query_args={
-				"field_filters": {"item_group": "Products"},
-				"attribute_filters": {"Test Size": ["Large"]},
-				"start": 0,
-			}
-		)
-
-		items = result.get("items")
-
-		self.assertEqual(len(items), 1)
-		self.assertEqual(items[0].get("item_code"), "Test Web Item-L")
-
-	def test_product_list_with_variants(self):
-		"Test if variants are hideen on hiding variants in settings."
-		create_variant_web_item()
-
-		setup_e_commerce_settings({"enable_attribute_filters": 0, "hide_variants": 1})
-		frappe.local.shopping_cart_settings = None
-
-		attribute_filters = {"Test Size": ["Large"]}
-		engine = ProductQuery()
-		result = engine.query(
-			attributes=attribute_filters, fields={}, search_term=None, start=0, item_group=None
-		)
-		items = result.get("items")
-
-		# check if any variants are fetched even though published variant exists
-		self.assertEqual(len(items), 0)
-
-		# tear down
-		setup_e_commerce_settings({"enable_attribute_filters": 1, "hide_variants": 0})
-
-	def test_custom_field_as_filter(self):
-		"Test if custom field functions as filter correctly."
-		from frappe.custom.doctype.custom_field.custom_field import create_custom_field
-
-		create_custom_field(
-			"Website Item",
-			dict(
-				owner="Administrator",
-				fieldname="supplier",
-				label="Supplier",
-				fieldtype="Link",
-				options="Supplier",
-				insert_after="on_backorder",
-			),
-		)
-
-		frappe.db.set_value(
-			"Website Item", {"item_code": "Test 11I Laptop"}, "supplier", "_Test Supplier"
-		)
-		frappe.db.set_value(
-			"Website Item", {"item_code": "Test 12I Laptop"}, "supplier", "_Test Supplier 1"
-		)
-
-		settings = frappe.get_doc("E Commerce Settings")
-		settings.append("filter_fields", {"fieldname": "supplier"})
-		settings.save()
-
-		filter_engine = ProductFiltersBuilder()
-		field_filters = filter_engine.get_field_filters()
-		custom_filter = field_filters[1]
-		filter_values = custom_filter[1]
-
-		self.assertEqual(custom_filter[0].options, "Supplier")
-		self.assertEqual(len(filter_values), 2)
-		self.assertIn("_Test Supplier", filter_values)
-
-		# test if custom filter works in query
-		field_filters = {"supplier": "_Test Supplier 1"}
-		engine = ProductQuery()
-		result = engine.query(
-			attributes={}, fields=field_filters, search_term=None, start=0, item_group=None
-		)
-		items = result.get("items")
-
-		# check if only 'Raw Material' are fetched in the right order
-		self.assertEqual(len(items), 1)
-		self.assertEqual(items[0].get("item_code"), "Test 12I Laptop")
-
-
-def create_variant_web_item():
-	"Create Variant and Template Website Items."
-	from erpnext.controllers.item_variant import create_variant
-	from erpnext.e_commerce.doctype.website_item.website_item import make_website_item
-	from erpnext.stock.doctype.item.test_item import make_item
-
-	make_item(
-		"Test Web Item",
-		{
-			"has_variant": 1,
-			"variant_based_on": "Item Attribute",
-			"attributes": [{"attribute": "Test Size"}],
-		},
-	)
-	if not frappe.db.exists("Item", "Test Web Item-L"):
-		variant = create_variant("Test Web Item", {"Test Size": "Large"})
-		variant.save()
-
-	if not frappe.db.exists("Website Item", {"variant_of": "Test Web Item"}):
-		make_website_item(variant, save=True)
diff --git a/erpnext/e_commerce/product_ui/grid.js b/erpnext/e_commerce/product_ui/grid.js
deleted file mode 100644
index 20a6c30..0000000
--- a/erpnext/e_commerce/product_ui/grid.js
+++ /dev/null
@@ -1,201 +0,0 @@
-erpnext.ProductGrid = class {
-	/* Options:
-		- items: Items
-		- settings: E Commerce Settings
-		- products_section: Products Wrapper
-		- preference: If preference is not grid view, render but hide
-	*/
-	constructor(options) {
-		Object.assign(this, options);
-
-		if (this.preference !== "Grid View") {
-			this.products_section.addClass("hidden");
-		}
-
-		this.products_section.empty();
-		this.make();
-	}
-
-	make() {
-		let me = this;
-		let html = ``;
-
-		this.items.forEach(item => {
-			let title = item.web_item_name || item.item_name || item.item_code || "";
-			title =  title.length > 90 ? title.substr(0, 90) + "..." : title;
-
-			html += `<div class="col-sm-4 item-card"><div class="card text-left">`;
-			html += me.get_image_html(item, title);
-			html += me.get_card_body_html(item, title, me.settings);
-			html += `</div></div>`;
-		});
-
-		let $product_wrapper = this.products_section;
-		$product_wrapper.append(html);
-	}
-
-	get_image_html(item, title) {
-		let image = item.website_image;
-
-		if (image) {
-			return `
-				<div class="card-img-container">
-					<a href="/${ item.route || '#' }" style="text-decoration: none;">
-						<img class="card-img" src="${ image }" alt="${ title }">
-					</a>
-				</div>
-			`;
-		} else {
-			return `
-				<div class="card-img-container">
-					<a href="/${ item.route || '#' }" style="text-decoration: none;">
-						<div class="card-img-top no-image">
-							${ frappe.get_abbr(title) }
-						</div>
-					</a>
-				</div>
-			`;
-		}
-	}
-
-	get_card_body_html(item, title, settings) {
-		let body_html = `
-			<div class="card-body text-left card-body-flex" style="width:100%">
-				<div style="margin-top: 1rem; display: flex;">
-		`;
-		body_html += this.get_title(item, title);
-
-		// get floating elements
-		if (!item.has_variants) {
-			if (settings.enable_wishlist) {
-				body_html += this.get_wishlist_icon(item);
-			}
-			if (settings.enabled) {
-				body_html += this.get_cart_indicator(item);
-			}
-
-		}
-
-		body_html += `</div>`;
-		body_html += `<div class="product-category">${ item.item_group || '' }</div>`;
-
-		if (item.formatted_price) {
-			body_html += this.get_price_html(item);
-		}
-
-		body_html += this.get_stock_availability(item, settings);
-		body_html += this.get_primary_button(item, settings);
-		body_html += `</div>`; // close div on line 49
-
-		return body_html;
-	}
-
-	get_title(item, title) {
-		let title_html = `
-			<a href="/${ item.route || '#' }">
-				<div class="product-title">
-					${ title || '' }
-				</div>
-			</a>
-		`;
-		return title_html;
-	}
-
-	get_wishlist_icon(item) {
-		let icon_class = item.wished ? "wished" : "not-wished";
-		return `
-			<div class="like-action ${ item.wished ? "like-action-wished" : ''}"
-				data-item-code="${ item.item_code }">
-				<svg class="icon sm">
-					<use class="${ icon_class } wish-icon" href="#icon-heart"></use>
-				</svg>
-			</div>
-		`;
-	}
-
-	get_cart_indicator(item) {
-		return `
-			<div class="cart-indicator ${item.in_cart ? '' : 'hidden'}" data-item-code="${ item.item_code }">
-				1
-			</div>
-		`;
-	}
-
-	get_price_html(item) {
-		let price_html = `
-			<div class="product-price">
-				${ item.formatted_price || '' }
-		`;
-
-		if (item.formatted_mrp) {
-			price_html += `
-				<small class="striked-price">
-					<s>${ item.formatted_mrp ? item.formatted_mrp.replace(/ +/g, "") : "" }</s>
-				</small>
-				<small class="ml-1 product-info-green">
-					${ item.discount } OFF
-				</small>
-			`;
-		}
-		price_html += `</div>`;
-		return price_html;
-	}
-
-	get_stock_availability(item, settings) {
-		if (settings.show_stock_availability && !item.has_variants) {
-			if (item.on_backorder) {
-				return `
-					<span class="out-of-stock mb-2 mt-1" style="color: var(--primary-color)">
-						${ __("Available on backorder") }
-					</span>
-				`;
-			} else if (!item.in_stock) {
-				return `
-					<span class="out-of-stock mb-2 mt-1">
-						${ __("Out of stock") }
-					</span>
-				`;
-			}
-		}
-
-		return ``;
-	}
-
-	get_primary_button(item, settings) {
-		if (item.has_variants) {
-			return `
-				<a href="/${ item.route || '#' }">
-					<div class="btn btn-sm btn-explore-variants w-100 mt-4">
-						${ __('Explore') }
-					</div>
-				</a>
-			`;
-		} else if (settings.enabled && (settings.allow_items_not_in_stock || item.in_stock)) {
-			return `
-				<div id="${ item.name }" class="btn
-					btn-sm btn-primary btn-add-to-cart-list
-					w-100 mt-2 ${ item.in_cart ? 'hidden' : '' }"
-					data-item-code="${ item.item_code }">
-					<span class="mr-2">
-						<svg class="icon icon-md">
-							<use href="#icon-assets"></use>
-						</svg>
-					</span>
-					${ settings.enable_checkout ? __('Add to Cart') :  __('Add to Quote') }
-				</div>
-
-				<a href="/cart">
-					<div id="${ item.name }" class="btn
-						btn-sm btn-primary btn-add-to-cart-list
-						w-100 mt-4 go-to-cart-grid
-						${ item.in_cart ? '' : 'hidden' }"
-						data-item-code="${ item.item_code }">
-						${ settings.enable_checkout ? __('Go to Cart') :  __('Go to Quote') }
-					</div>
-				</a>
-			`;
-		} else {
-			return ``;
-		}
-	}
-};
\ No newline at end of file
diff --git a/erpnext/e_commerce/product_ui/list.js b/erpnext/e_commerce/product_ui/list.js
deleted file mode 100644
index c8fd767..0000000
--- a/erpnext/e_commerce/product_ui/list.js
+++ /dev/null
@@ -1,205 +0,0 @@
-erpnext.ProductList = class {
-	/* Options:
-		- items: Items
-		- settings: E Commerce Settings
-		- products_section: Products Wrapper
-		- preference: If preference is not list view, render but hide
-	*/
-	constructor(options) {
-		Object.assign(this, options);
-
-		if (this.preference !== "List View") {
-			this.products_section.addClass("hidden");
-		}
-
-		this.products_section.empty();
-		this.make();
-	}
-
-	make() {
-		let me = this;
-		let html = `<br><br>`;
-
-		this.items.forEach(item => {
-			let title = item.web_item_name || item.item_name || item.item_code || "";
-			title =  title.length > 200 ? title.substr(0, 200) + "..." : title;
-
-			html += `<div class='row list-row w-100 mb-4'>`;
-			html += me.get_image_html(item, title, me.settings);
-			html += me.get_row_body_html(item, title, me.settings);
-			html += `</div>`;
-		});
-
-		let $product_wrapper = this.products_section;
-		$product_wrapper.append(html);
-	}
-
-	get_image_html(item, title, settings) {
-		let image = item.website_image;
-		let wishlist_enabled = !item.has_variants && settings.enable_wishlist;
-		let image_html = ``;
-
-		if (image) {
-			image_html += `
-				<div class="col-2 border text-center rounded list-image">
-					<a class="product-link product-list-link" href="/${ item.route || '#' }">
-						<img itemprop="image" class="website-image h-100 w-100" alt="${ title }"
-							src="${ image }">
-					</a>
-					${ wishlist_enabled ? this.get_wishlist_icon(item): '' }
-				</div>
-			`;
-		} else {
-			image_html += `
-				<div class="col-2 border text-center rounded list-image">
-					<a class="product-link product-list-link" href="/${ item.route || '#' }"
-						style="text-decoration: none">
-						<div class="card-img-top no-image-list">
-							${ frappe.get_abbr(title) }
-						</div>
-					</a>
-					${ wishlist_enabled ? this.get_wishlist_icon(item): '' }
-				</div>
-			`;
-		}
-
-		return image_html;
-	}
-
-	get_row_body_html(item, title, settings) {
-		let body_html = `<div class='col-10 text-left'>`;
-		body_html += this.get_title_html(item, title, settings);
-		body_html += this.get_item_details(item, settings);
-		body_html += `</div>`;
-		return body_html;
-	}
-
-	get_title_html(item, title, settings) {
-		let title_html = `<div style="display: flex; margin-left: -15px;">`;
-		title_html += `
-			<div class="col-8" style="margin-right: -15px;">
-				<a href="/${ item.route || '#' }">
-					<div class="product-title">
-					${ title }
-					</div>
-				</a>
-			</div>
-		`;
-
-		if (settings.enabled) {
-			title_html += `<div class="col-4 cart-action-container ${item.in_cart ? 'd-flex' : ''}">`;
-			title_html += this.get_primary_button(item, settings);
-			title_html += `</div>`;
-		}
-		title_html += `</div>`;
-
-		return title_html;
-	}
-
-	get_item_details(item, settings) {
-		let details = `
-			<p class="product-code">
-				${ item.item_group } | Item Code : ${ item.item_code }
-			</p>
-			<div class="mt-2" style="color: var(--gray-600) !important; font-size: 13px;">
-				${ item.short_description || '' }
-			</div>
-			<div class="product-price">
-				${ item.formatted_price || '' }
-		`;
-
-		if (item.formatted_mrp) {
-			details += `
-				<small class="striked-price">
-					<s>${ item.formatted_mrp ? item.formatted_mrp.replace(/ +/g, "") : "" }</s>
-				</small>
-				<small class="ml-1 product-info-green">
-					${ item.discount } OFF
-				</small>
-			`;
-		}
-
-		details += this.get_stock_availability(item, settings);
-		details += `</div>`;
-
-		return details;
-	}
-
-	get_stock_availability(item, settings) {
-		if (settings.show_stock_availability && !item.has_variants) {
-			if (item.on_backorder) {
-				return `
-					<br>
-					<span class="out-of-stock mt-2" style="color: var(--primary-color)">
-						${ __("Available on backorder") }
-					</span>
-				`;
-			} else if (!item.in_stock) {
-				return `
-					<br>
-					<span class="out-of-stock mt-2">${ __("Out of stock") }</span>
-				`;
-			}
-		}
-		return ``;
-	}
-
-	get_wishlist_icon(item) {
-		let icon_class = item.wished ? "wished" : "not-wished";
-
-		return `
-			<div class="like-action-list ${ item.wished ? "like-action-wished" : ''}"
-				data-item-code="${ item.item_code }">
-				<svg class="icon sm">
-					<use class="${ icon_class } wish-icon" href="#icon-heart"></use>
-				</svg>
-			</div>
-		`;
-	}
-
-	get_primary_button(item, settings) {
-		if (item.has_variants) {
-			return `
-				<a href="/${ item.route || '#' }">
-					<div class="btn btn-sm btn-explore-variants btn mb-0 mt-0">
-						${ __('Explore') }
-					</div>
-				</a>
-			`;
-		} else if (settings.enabled && (settings.allow_items_not_in_stock || item.in_stock)) {
-			return `
-				<div id="${ item.name }" class="btn
-					btn-sm btn-primary btn-add-to-cart-list mb-0
-					${ item.in_cart ? 'hidden' : '' }"
-					data-item-code="${ item.item_code }"
-					style="margin-top: 0px !important; max-height: 30px; float: right;
-						padding: 0.25rem 1rem; min-width: 135px;">
-					<span class="mr-2">
-						<svg class="icon icon-md">
-							<use href="#icon-assets"></use>
-						</svg>
-					</span>
-					${ settings.enable_checkout ? __('Add to Cart') :  __('Add to Quote') }
-				</div>
-
-				<div class="cart-indicator list-indicator ${item.in_cart ? '' : 'hidden'}">
-					1
-				</div>
-
-				<a href="/cart">
-					<div id="${ item.name }" class="btn
-						btn-sm btn-primary btn-add-to-cart-list
-						ml-4 go-to-cart mb-0 mt-0
-						${ item.in_cart ? '' : 'hidden' }"
-						data-item-code="${ item.item_code }"
-						style="padding: 0.25rem 1rem; min-width: 135px;">
-						${ settings.enable_checkout ? __('Go to Cart') :  __('Go to Quote') }
-					</div>
-				</a>
-			`;
-		} else {
-			return ``;
-		}
-	}
-
-};
diff --git a/erpnext/e_commerce/product_ui/search.js b/erpnext/e_commerce/product_ui/search.js
deleted file mode 100644
index 1688cc1..0000000
--- a/erpnext/e_commerce/product_ui/search.js
+++ /dev/null
@@ -1,244 +0,0 @@
-erpnext.ProductSearch = class {
-	constructor(opts) {
-		/* Options: search_box_id (for custom search box) */
-		$.extend(this, opts);
-		this.MAX_RECENT_SEARCHES = 4;
-		this.search_box_id = this.search_box_id || "#search-box";
-		this.searchBox = $(this.search_box_id);
-
-		this.setupSearchDropDown();
-		this.bindSearchAction();
-	}
-
-	setupSearchDropDown() {
-		this.search_area = $("#dropdownMenuSearch");
-		this.setupSearchResultContainer();
-		this.populateRecentSearches();
-	}
-
-	bindSearchAction() {
-		let me = this;
-
-		// Show Search dropdown
-		this.searchBox.on("focus", () => {
-			this.search_dropdown.removeClass("hidden");
-		});
-
-		// If click occurs outside search input/results, hide results.
-		// Click can happen anywhere on the page
-		$("body").on("click", (e) => {
-			let searchEvent = $(e.target).closest(this.search_box_id).length;
-			let resultsEvent = $(e.target).closest('#search-results-container').length;
-			let isResultHidden = this.search_dropdown.hasClass("hidden");
-
-			if (!searchEvent && !resultsEvent && !isResultHidden) {
-				this.search_dropdown.addClass("hidden");
-			}
-		});
-
-		// Process search input
-		this.searchBox.on("input", (e) => {
-			let query = e.target.value;
-
-			if (query.length == 0) {
-				me.populateResults(null);
-				me.populateCategoriesList(null);
-			}
-
-			if (query.length < 3 || !query.length) return;
-
-			frappe.call({
-				method: "erpnext.templates.pages.product_search.search",
-				args: {
-					query: query
-				},
-				callback: (data) => {
-					let product_results = null, category_results = null;
-
-					// Populate product results
-					product_results = data.message ? data.message.product_results : null;
-					me.populateResults(product_results);
-
-					// Populate categories
-					if (me.category_container) {
-						category_results = data.message ? data.message.category_results : null;
-						me.populateCategoriesList(category_results);
-					}
-
-					// Populate recent search chips only on successful queries
-					if (!$.isEmptyObject(product_results) || !$.isEmptyObject(category_results)) {
-						me.setRecentSearches(query);
-					}
-				}
-			});
-
-			this.search_dropdown.removeClass("hidden");
-		});
-	}
-
-	setupSearchResultContainer() {
-		this.search_dropdown = this.search_area.append(`
-			<div class="overflow-hidden shadow dropdown-menu w-100 hidden"
-				id="search-results-container"
-				aria-labelledby="dropdownMenuSearch"
-				style="display: flex; flex-direction: column;">
-			</div>
-		`).find("#search-results-container");
-
-		this.setupCategoryContainer();
-		this.setupProductsContainer();
-		this.setupRecentsContainer();
-	}
-
-	setupProductsContainer() {
-		this.products_container = this.search_dropdown.append(`
-			<div id="product-results mt-2">
-				<div id="product-scroll" style="overflow: scroll; max-height: 300px">
-				</div>
-			</div>
-		`).find("#product-scroll");
-	}
-
-	setupCategoryContainer() {
-		this.category_container = this.search_dropdown.append(`
-			<div class="category-container mt-2 mb-1">
-				<div class="category-chips">
-				</div>
-			</div>
-		`).find(".category-chips");
-	}
-
-	setupRecentsContainer() {
-		let $recents_section = this.search_dropdown.append(`
-			<div class="mb-2 mt-2 recent-searches">
-				<div>
-					<b>${ __("Recent") }</b>
-				</div>
-			</div>
-		`).find(".recent-searches");
-
-		this.recents_container = $recents_section.append(`
-			<div id="recents" style="padding: .25rem 0 1rem 0;">
-			</div>
-		`).find("#recents");
-	}
-
-	getRecentSearches() {
-		return JSON.parse(localStorage.getItem("recent_searches") || "[]");
-	}
-
-	attachEventListenersToChips() {
-		let me  = this;
-		const chips = $(".recent-search");
-		window.chips = chips;
-
-		for (let chip of chips) {
-			chip.addEventListener("click", () => {
-				me.searchBox[0].value = chip.innerText.trim();
-
-				// Start search with `recent query`
-				me.searchBox.trigger("input");
-				me.searchBox.focus();
-			});
-		}
-	}
-
-	setRecentSearches(query) {
-		let recents = this.getRecentSearches();
-		if (recents.length >= this.MAX_RECENT_SEARCHES) {
-			// Remove the `first` query
-			recents.splice(0, 1);
-		}
-
-		if (recents.indexOf(query) >= 0) {
-			return;
-		}
-
-		recents.push(query);
-		localStorage.setItem("recent_searches", JSON.stringify(recents));
-
-		this.populateRecentSearches();
-	}
-
-	populateRecentSearches() {
-		let recents = this.getRecentSearches();
-
-		if (!recents.length) {
-			this.recents_container.html(`<span class=""text-muted">No searches yet.</span>`);
-			return;
-		}
-
-		let html = "";
-		recents.forEach((key) => {
-			html += `
-				<div class="recent-search mr-1" style="font-size: 13px">
-					<span class="mr-2">
-						<svg width="20" height="20" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
-							<path d="M8 14C11.3137 14 14 11.3137 14 8C14 4.68629 11.3137 2 8 2C4.68629 2 2 4.68629 2 8C2 11.3137 4.68629 14 8 14Z" stroke="var(--gray-500)"" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
-							<path d="M8.00027 5.20947V8.00017L10 10" stroke="var(--gray-500)" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
-						</svg>
-					</span>
-					${ key }
-				</div>
-			`;
-		});
-
-		this.recents_container.html(html);
-		this.attachEventListenersToChips();
-	}
-
-	populateResults(product_results) {
-		if (!product_results || product_results.length === 0) {
-			let empty_html = ``;
-			this.products_container.html(empty_html);
-			return;
-		}
-
-		let html = "";
-
-		product_results.forEach((res) => {
-			let thumbnail = res.thumbnail || '/assets/erpnext/images/ui-states/cart-empty-state.png';
-			html += `
-				<div class="dropdown-item" style="display: flex;">
-					<img class="item-thumb col-2" src=${encodeURI(thumbnail)} />
-					<div class="col-9" style="white-space: normal;">
-						<a href="/${res.route}">${res.web_item_name}</a><br>
-						<span class="brand-line">${res.brand ? "by " + res.brand : ""}</span>
-					</div>
-				</div>
-			`;
-		});
-
-		this.products_container.html(html);
-	}
-
-	populateCategoriesList(category_results) {
-		if (!category_results || category_results.length === 0) {
-			let empty_html = `
-				<div class="category-container mt-2">
-					<div class="category-chips">
-					</div>
-				</div>
-			`;
-			this.category_container.html(empty_html);
-			return;
-		}
-
-		let html = `
-			<div class="mb-2">
-				<b>${ __("Categories") }</b>
-			</div>
-		`;
-
-		category_results.forEach((category) => {
-			html += `
-				<a href="/${category.route}" class="btn btn-sm category-chip mr-2 mb-2"
-					style="font-size: 13px" role="button">
-				${ category.name }
-				</button>
-			`;
-		});
-
-		this.category_container.html(html);
-	}
-};
diff --git a/erpnext/e_commerce/product_ui/views.js b/erpnext/e_commerce/product_ui/views.js
deleted file mode 100644
index fb63b21..0000000
--- a/erpnext/e_commerce/product_ui/views.js
+++ /dev/null
@@ -1,548 +0,0 @@
-erpnext.ProductView =  class {
-	/* Options:
-		- View Type
-		- Products Section Wrapper,
-		- Item Group: If its an Item Group page
-	*/
-	constructor(options) {
-		Object.assign(this, options);
-		this.preference = this.view_type;
-		this.make();
-	}
-
-	make(from_filters=false) {
-		this.products_section.empty();
-		this.prepare_toolbar();
-		this.get_item_filter_data(from_filters);
-	}
-
-	prepare_toolbar() {
-		this.products_section.append(`
-			<div class="toolbar d-flex">
-			</div>
-		`);
-		this.prepare_search();
-		this.prepare_view_toggler();
-
-		new erpnext.ProductSearch();
-	}
-
-	prepare_view_toggler() {
-
-		if (!$("#list").length || !$("#image-view").length) {
-			this.render_view_toggler();
-			this.bind_view_toggler_actions();
-			this.set_view_state();
-		}
-	}
-
-	get_item_filter_data(from_filters=false) {
-		// Get and render all Product related views
-		let me = this;
-		this.from_filters = from_filters;
-		let args = this.get_query_filters();
-
-		this.disable_view_toggler(true);
-
-		frappe.call({
-			method: "erpnext.e_commerce.api.get_product_filter_data",
-			args: {
-				query_args: args
-			},
-			callback: function(result) {
-				if (!result || result.exc || !result.message || result.message.exc) {
-					me.render_no_products_section(true);
-				} else {
-					// Sub Category results are independent of Items
-					if (me.item_group && result.message["sub_categories"].length) {
-						me.render_item_sub_categories(result.message["sub_categories"]);
-					}
-
-					if (!result.message["items"].length) {
-						// if result has no items or result is empty
-						me.render_no_products_section();
-					} else {
-						// Add discount filters
-						me.re_render_discount_filters(result.message["filters"].discount_filters);
-
-						// Render views
-						me.render_list_view(result.message["items"], result.message["settings"]);
-						me.render_grid_view(result.message["items"], result.message["settings"]);
-
-						me.products = result.message["items"];
-						me.product_count = result.message["items_count"];
-					}
-
-					// Bind filter actions
-					if (!from_filters) {
-						// If `get_product_filter_data` was triggered after checking a filter,
-						// don't touch filters unnecessarily, only data must change
-						// filter persistence is handle on filter change event
-						me.bind_filters();
-						me.restore_filters_state();
-					}
-
-					// Bottom paging
-					me.add_paging_section(result.message["settings"]);
-				}
-
-				me.disable_view_toggler(false);
-			}
-		});
-	}
-
-	disable_view_toggler(disable=false) {
-		$('#list').prop('disabled', disable);
-		$('#image-view').prop('disabled', disable);
-	}
-
-	render_grid_view(items, settings) {
-		// loop over data and add grid html to it
-		let me = this;
-		this.prepare_product_area_wrapper("grid");
-
-		new erpnext.ProductGrid({
-			items: items,
-			products_section: $("#products-grid-area"),
-			settings: settings,
-			preference: me.preference
-		});
-	}
-
-	render_list_view(items, settings) {
-		let me = this;
-		this.prepare_product_area_wrapper("list");
-
-		new erpnext.ProductList({
-			items: items,
-			products_section: $("#products-list-area"),
-			settings: settings,
-			preference: me.preference
-		});
-	}
-
-	prepare_product_area_wrapper(view) {
-		let left_margin = view == "list" ? "ml-2" : "";
-		let top_margin = view == "list" ? "mt-6" : "mt-minus-1";
-		return this.products_section.append(`
-			<br>
-			<div id="products-${view}-area" class="row products-list ${ top_margin } ${ left_margin }"></div>
-		`);
-	}
-
-	get_query_filters() {
-		const filters = frappe.utils.get_query_params();
-		let {field_filters, attribute_filters} = filters;
-
-		field_filters = field_filters ? JSON.parse(field_filters) : {};
-		attribute_filters = attribute_filters ? JSON.parse(attribute_filters) : {};
-
-		return {
-			field_filters: field_filters,
-			attribute_filters: attribute_filters,
-			item_group: this.item_group,
-			start: filters.start || null,
-			from_filters: this.from_filters || false
-		};
-	}
-
-	add_paging_section(settings) {
-		$(".product-paging-area").remove();
-
-		if (this.products) {
-			let paging_html = `
-				<div class="row product-paging-area mt-5">
-					<div class="col-3">
-					</div>
-					<div class="col-9 text-right">
-			`;
-			let query_params = frappe.utils.get_query_params();
-			let start = query_params.start ? cint(JSON.parse(query_params.start)) : 0;
-			let page_length = settings.products_per_page || 0;
-
-			let prev_disable = start > 0 ? "" : "disabled";
-			let next_disable = (this.product_count > page_length) ? "" : "disabled";
-
-			paging_html += `
-				<button class="btn btn-default btn-prev" data-start="${ start - page_length }"
-					style="float: left" ${prev_disable}>
-					${ __("Prev") }
-				</button>`;
-
-			paging_html += `
-				<button class="btn btn-default btn-next" data-start="${ start + page_length }"
-					${next_disable}>
-					${ __("Next") }
-				</button>
-			`;
-
-			paging_html += `</div></div>`;
-
-			$(".page_content").append(paging_html);
-			this.bind_paging_action();
-		}
-	}
-
-	prepare_search() {
-		$(".toolbar").append(`
-			<div class="input-group col-8 p-0">
-				<div class="dropdown w-100" id="dropdownMenuSearch">
-					<input type="search" name="query" id="search-box" class="form-control font-md"
-						placeholder="Search for Products"
-						aria-label="Product" aria-describedby="button-addon2">
-					<div class="search-icon">
-						<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"
-							fill="none"
-							stroke="currentColor" stroke-width="2" stroke-linecap="round"
-							stroke-linejoin="round"
-							class="feather feather-search">
-							<circle cx="11" cy="11" r="8"></circle>
-							<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
-						</svg>
-					</div>
-					<!-- Results dropdown rendered in product_search.js -->
-				</div>
-			</div>
-		`);
-	}
-
-	render_view_toggler() {
-		$(".toolbar").append(`<div class="toggle-container col-4 p-0"></div>`);
-
-		["btn-list-view", "btn-grid-view"].forEach(view => {
-			let icon = view === "btn-list-view" ? "list" : "image-view";
-			$(".toggle-container").append(`
-				<div class="form-group mb-0" id="toggle-view">
-					<button id="${ icon }" class="btn ${ view } mr-2">
-						<span>
-							<svg class="icon icon-md">
-								<use href="#icon-${ icon }"></use>
-							</svg>
-						</span>
-					</button>
-				</div>
-			`);
-		});
-	}
-
-	bind_view_toggler_actions() {
-		$("#list").click(function() {
-			let $btn = $(this);
-			$btn.removeClass('btn-primary');
-			$btn.addClass('btn-primary');
-			$(".btn-grid-view").removeClass('btn-primary');
-
-			$("#products-grid-area").addClass("hidden");
-			$("#products-list-area").removeClass("hidden");
-			localStorage.setItem("product_view", "List View");
-		});
-
-		$("#image-view").click(function() {
-			let $btn = $(this);
-			$btn.removeClass('btn-primary');
-			$btn.addClass('btn-primary');
-			$(".btn-list-view").removeClass('btn-primary');
-
-			$("#products-list-area").addClass("hidden");
-			$("#products-grid-area").removeClass("hidden");
-			localStorage.setItem("product_view", "Grid View");
-		});
-	}
-
-	set_view_state() {
-		if (this.preference === "List View") {
-			$("#list").addClass('btn-primary');
-			$("#image-view").removeClass('btn-primary');
-		} else {
-			$("#image-view").addClass('btn-primary');
-			$("#list").removeClass('btn-primary');
-		}
-	}
-
-	bind_paging_action() {
-		let me = this;
-		$('.btn-prev, .btn-next').click((e) => {
-			const $btn = $(e.target);
-			me.from_filters = false;
-
-			$btn.prop('disabled', true);
-			const start = $btn.data('start');
-
-			let query_params = frappe.utils.get_query_params();
-			query_params.start = start;
-			let path = window.location.pathname + '?' + frappe.utils.get_url_from_dict(query_params);
-			window.location.href = path;
-		});
-	}
-
-	re_render_discount_filters(filter_data) {
-		this.get_discount_filter_html(filter_data);
-		if (this.from_filters) {
-			// Bind filter action if triggered via filters
-			// if not from filter action, page load will bind actions
-			this.bind_discount_filter_action();
-		}
-		// discount filters are rendered with Items (later)
-		// unlike the other filters
-		this.restore_discount_filter();
-	}
-
-	get_discount_filter_html(filter_data) {
-		$("#discount-filters").remove();
-		if (filter_data) {
-			$("#product-filters").append(`
-				<div id="discount-filters" class="mb-4 filter-block pb-5">
-					<div class="filter-label mb-3">${ __("Discounts") }</div>
-				</div>
-			`);
-
-			let html = `<div class="filter-options">`;
-			filter_data.forEach(filter => {
-				html += `
-					<div class="checkbox">
-						<label data-value="${ filter[0] }">
-							<input type="radio"
-								class="product-filter discount-filter"
-								name="discount" id="${ filter[0] }"
-								data-filter-name="discount"
-								data-filter-value="${ filter[0] }"
-								style="width: 14px !important"
-							>
-								<span class="label-area" for="${ filter[0] }">
-									${ filter[1] }
-								</span>
-						</label>
-					</div>
-				`;
-			});
-			html += `</div>`;
-
-			$("#discount-filters").append(html);
-		}
-	}
-
-	restore_discount_filter() {
-		const filters = frappe.utils.get_query_params();
-		let field_filters = filters.field_filters;
-		if (!field_filters) return;
-
-		field_filters = JSON.parse(field_filters);
-
-		if (field_filters && field_filters["discount"]) {
-			const values = field_filters["discount"];
-			const selector = values.map(value => {
-				return `input[data-filter-name="discount"][data-filter-value="${value}"]`;
-			}).join(',');
-			$(selector).prop('checked', true);
-			this.field_filters = field_filters;
-		}
-	}
-
-	bind_discount_filter_action() {
-		let me = this;
-		$('.discount-filter').on('change', (e) => {
-			const $checkbox = $(e.target);
-			const is_checked = $checkbox.is(':checked');
-
-			const {
-				filterValue: filter_value
-			} = $checkbox.data();
-
-			delete this.field_filters["discount"];
-
-			if (is_checked) {
-				this.field_filters["discount"] = [];
-				this.field_filters["discount"].push(filter_value);
-			}
-
-			if (this.field_filters["discount"].length === 0) {
-				delete this.field_filters["discount"];
-			}
-
-			me.change_route_with_filters();
-		});
-	}
-
-	bind_filters() {
-		let me = this;
-		this.field_filters = {};
-		this.attribute_filters = {};
-
-		$('.product-filter').on('change', (e) => {
-			me.from_filters = true;
-
-			const $checkbox = $(e.target);
-			const is_checked = $checkbox.is(':checked');
-
-			if ($checkbox.is('.attribute-filter')) {
-				const {
-					attributeName: attribute_name,
-					attributeValue: attribute_value
-				} = $checkbox.data();
-
-				if (is_checked) {
-					this.attribute_filters[attribute_name] = this.attribute_filters[attribute_name] || [];
-					this.attribute_filters[attribute_name].push(attribute_value);
-				} else {
-					this.attribute_filters[attribute_name] = this.attribute_filters[attribute_name] || [];
-					this.attribute_filters[attribute_name] = this.attribute_filters[attribute_name].filter(v => v !== attribute_value);
-				}
-
-				if (this.attribute_filters[attribute_name].length === 0) {
-					delete this.attribute_filters[attribute_name];
-				}
-			} else if ($checkbox.is('.field-filter') || $checkbox.is('.discount-filter')) {
-				const {
-					filterName: filter_name,
-					filterValue: filter_value
-				} = $checkbox.data();
-
-				if ($checkbox.is('.discount-filter')) {
-					// clear previous discount filter to accomodate new
-					delete this.field_filters["discount"];
-				}
-				if (is_checked) {
-					this.field_filters[filter_name] = this.field_filters[filter_name] || [];
-					if (!in_list(this.field_filters[filter_name], filter_value)) {
-						this.field_filters[filter_name].push(filter_value);
-					}
-				} else {
-					this.field_filters[filter_name] = this.field_filters[filter_name] || [];
-					this.field_filters[filter_name] = this.field_filters[filter_name].filter(v => v !== filter_value);
-				}
-
-				if (this.field_filters[filter_name].length === 0) {
-					delete this.field_filters[filter_name];
-				}
-			}
-
-			me.change_route_with_filters();
-		});
-
-		// bind filter lookup input box
-		$('.filter-lookup-input').on('keydown', frappe.utils.debounce((e) => {
-			const $input = $(e.target);
-			const keyword = ($input.val() || '').toLowerCase();
-			const $filter_options = $input.next('.filter-options');
-
-			$filter_options.find('.filter-lookup-wrapper').show();
-			$filter_options.find('.filter-lookup-wrapper').each((i, el) => {
-				const $el = $(el);
-				const value = $el.data('value').toLowerCase();
-				if (!value.includes(keyword)) {
-					$el.hide();
-				}
-			});
-		}, 300));
-	}
-
-	change_route_with_filters() {
-		let route_params = frappe.utils.get_query_params();
-
-		let start = this.if_key_exists(route_params.start) || 0;
-		if (this.from_filters) {
-			start = 0; // show items from first page if new filters are triggered
-		}
-
-		const query_string = this.get_query_string({
-			start: start,
-			field_filters: JSON.stringify(this.if_key_exists(this.field_filters)),
-			attribute_filters: JSON.stringify(this.if_key_exists(this.attribute_filters)),
-		});
-		window.history.pushState('filters', '', `${location.pathname}?` + query_string);
-
-		$('.page_content input').prop('disabled', true);
-
-		this.make(true);
-		$('.page_content input').prop('disabled', false);
-	}
-
-	restore_filters_state() {
-		const filters = frappe.utils.get_query_params();
-		let {field_filters, attribute_filters} = filters;
-
-		if (field_filters) {
-			field_filters = JSON.parse(field_filters);
-			for (let fieldname in field_filters) {
-				const values = field_filters[fieldname];
-				const selector = values.map(value => {
-					return `input[data-filter-name="${fieldname}"][data-filter-value="${value}"]`;
-				}).join(',');
-				$(selector).prop('checked', true);
-			}
-			this.field_filters = field_filters;
-		}
-		if (attribute_filters) {
-			attribute_filters = JSON.parse(attribute_filters);
-			for (let attribute in attribute_filters) {
-				const values = attribute_filters[attribute];
-				const selector = values.map(value => {
-					return `input[data-attribute-name="${attribute}"][data-attribute-value="${value}"]`;
-				}).join(',');
-				$(selector).prop('checked', true);
-			}
-			this.attribute_filters = attribute_filters;
-		}
-	}
-
-	render_no_products_section(error=false) {
-		let error_section = `
-			<div class="mt-4 w-100 alert alert-error font-md">
-				Something went wrong. Please refresh or contact us.
-			</div>
-		`;
-		let no_results_section = `
-			<div class="cart-empty frappe-card mt-4">
-				<div class="cart-empty-state">
-					<img src="/assets/erpnext/images/ui-states/cart-empty-state.png" alt="Empty Cart">
-				</div>
-				<div class="cart-empty-message mt-4">${ __('No products found') }</p>
-			</div>
-		`;
-
-		this.products_section.append(error ? error_section : no_results_section);
-	}
-
-	render_item_sub_categories(categories) {
-		if (categories && categories.length) {
-			let sub_group_html = `
-				<div class="sub-category-container scroll-categories">
-			`;
-
-			categories.forEach(category => {
-				sub_group_html += `
-					<a href="/${ category.route || '#' }" style="text-decoration: none;">
-						<div class="category-pill">
-							${ category.name }
-						</div>
-					</a>
-				`;
-			});
-			sub_group_html += `</div>`;
-
-			$("#product-listing").prepend(sub_group_html);
-		}
-	}
-
-	get_query_string(object) {
-		const url = new URLSearchParams();
-		for (let key in object) {
-			const value = object[key];
-			if (value) {
-				url.append(key, value);
-			}
-		}
-		return url.toString();
-	}
-
-	if_key_exists(obj) {
-		let exists = false;
-		for (let key in obj) {
-			if (Object.prototype.hasOwnProperty.call(obj, key) && obj[key]) {
-				exists = true;
-				break;
-			}
-		}
-		return exists ? obj : undefined;
-	}
-};
\ No newline at end of file
diff --git a/erpnext/e_commerce/redisearch_utils.py b/erpnext/e_commerce/redisearch_utils.py
deleted file mode 100644
index 87ca9bd..0000000
--- a/erpnext/e_commerce/redisearch_utils.py
+++ /dev/null
@@ -1,255 +0,0 @@
-# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import json
-
-import frappe
-from frappe import _
-from frappe.utils.redis_wrapper import RedisWrapper
-from redis import ResponseError
-from redis.commands.search.field import TagField, TextField
-from redis.commands.search.indexDefinition import IndexDefinition
-from redis.commands.search.suggestion import Suggestion
-
-WEBSITE_ITEM_INDEX = "website_items_index"
-WEBSITE_ITEM_KEY_PREFIX = "website_item:"
-WEBSITE_ITEM_NAME_AUTOCOMPLETE = "website_items_name_dict"
-WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE = "website_items_category_dict"
-
-
-def get_indexable_web_fields():
-	"Return valid fields from Website Item that can be searched for."
-	web_item_meta = frappe.get_meta("Website Item", cached=True)
-	valid_fields = filter(
-		lambda df: df.fieldtype in ("Link", "Table MultiSelect", "Data", "Small Text", "Text Editor"),
-		web_item_meta.fields,
-	)
-
-	return [df.fieldname for df in valid_fields]
-
-
-def is_redisearch_enabled():
-	"Return True only if redisearch is loaded and enabled."
-	is_redisearch_enabled = frappe.db.get_single_value("E Commerce Settings", "is_redisearch_enabled")
-	return is_search_module_loaded() and is_redisearch_enabled
-
-
-def is_search_module_loaded():
-	try:
-		cache = frappe.cache()
-		for module in cache.module_list():
-			if module.get(b"name") == b"search":
-				return True
-	except Exception:
-		return False  # handling older redis versions
-
-
-def if_redisearch_enabled(function):
-	"Decorator to check if Redisearch is enabled."
-
-	def wrapper(*args, **kwargs):
-		if is_redisearch_enabled():
-			func = function(*args, **kwargs)
-			return func
-		return
-
-	return wrapper
-
-
-def make_key(key):
-	return frappe.cache().make_key(key)
-
-
-@if_redisearch_enabled
-def create_website_items_index():
-	"Creates Index Definition."
-
-	redis = frappe.cache()
-	index = redis.ft(WEBSITE_ITEM_INDEX)
-
-	try:
-		index.dropindex()  # drop if already exists
-	except ResponseError:
-		# will most likely raise a ResponseError if index does not exist
-		# ignore and create index
-		pass
-	except Exception:
-		raise_redisearch_error()
-
-	idx_def = IndexDefinition([make_key(WEBSITE_ITEM_KEY_PREFIX)])
-
-	# Index fields mentioned in e-commerce settings
-	idx_fields = frappe.db.get_single_value("E Commerce Settings", "search_index_fields")
-	idx_fields = idx_fields.split(",") if idx_fields else []
-
-	if "web_item_name" in idx_fields:
-		idx_fields.remove("web_item_name")
-
-	idx_fields = [to_search_field(f) for f in idx_fields]
-
-	# TODO: sortable?
-	index.create_index(
-		[TextField("web_item_name", sortable=True)] + idx_fields,
-		definition=idx_def,
-	)
-
-	reindex_all_web_items()
-	define_autocomplete_dictionary()
-
-
-def to_search_field(field):
-	if field == "tags":
-		return TagField("tags", separator=",")
-
-	return TextField(field)
-
-
-@if_redisearch_enabled
-def insert_item_to_index(website_item_doc):
-	# Insert item to index
-	key = get_cache_key(website_item_doc.name)
-	cache = frappe.cache()
-	web_item = create_web_item_map(website_item_doc)
-
-	for field, value in web_item.items():
-		super(RedisWrapper, cache).hset(make_key(key), field, value)
-
-	insert_to_name_ac(website_item_doc.web_item_name, website_item_doc.name)
-
-
-@if_redisearch_enabled
-def insert_to_name_ac(web_name, doc_name):
-	ac = frappe.cache().ft()
-	ac.sugadd(WEBSITE_ITEM_NAME_AUTOCOMPLETE, Suggestion(web_name, payload=doc_name))
-
-
-def create_web_item_map(website_item_doc):
-	fields_to_index = get_fields_indexed()
-	web_item = {}
-
-	for field in fields_to_index:
-		web_item[field] = website_item_doc.get(field) or ""
-
-	return web_item
-
-
-@if_redisearch_enabled
-def update_index_for_item(website_item_doc):
-	# Reinsert to Cache
-	insert_item_to_index(website_item_doc)
-	define_autocomplete_dictionary()
-
-
-@if_redisearch_enabled
-def delete_item_from_index(website_item_doc):
-	cache = frappe.cache()
-	key = get_cache_key(website_item_doc.name)
-
-	try:
-		cache.delete(key)
-	except Exception:
-		raise_redisearch_error()
-
-	delete_from_ac_dict(website_item_doc)
-	return True
-
-
-@if_redisearch_enabled
-def delete_from_ac_dict(website_item_doc):
-	"""Removes this items's name from autocomplete dictionary"""
-	ac = frappe.cache().ft()
-	ac.sugdel(website_item_doc.web_item_name)
-
-
-@if_redisearch_enabled
-def define_autocomplete_dictionary():
-	"""
-	Defines/Redefines an autocomplete search dictionary for Website Item Name.
-	Also creats autocomplete dictionary for Published Item Groups.
-	"""
-
-	cache = frappe.cache()
-
-	# Delete both autocomplete dicts
-	try:
-		cache.delete(make_key(WEBSITE_ITEM_NAME_AUTOCOMPLETE))
-		cache.delete(make_key(WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE))
-	except Exception:
-		raise_redisearch_error()
-
-	create_items_autocomplete_dict()
-	create_item_groups_autocomplete_dict()
-
-
-@if_redisearch_enabled
-def create_items_autocomplete_dict():
-	"Add items as suggestions in Autocompleter."
-
-	ac = frappe.cache().ft()
-	items = frappe.get_all(
-		"Website Item", fields=["web_item_name", "item_group"], filters={"published": 1}
-	)
-	for item in items:
-		ac.sugadd(WEBSITE_ITEM_NAME_AUTOCOMPLETE, Suggestion(item.web_item_name))
-
-
-@if_redisearch_enabled
-def create_item_groups_autocomplete_dict():
-	"Add item groups with weightage as suggestions in Autocompleter."
-
-	published_item_groups = frappe.get_all(
-		"Item Group", fields=["name", "route", "weightage"], filters={"show_in_website": 1}
-	)
-	if not published_item_groups:
-		return
-
-	ac = frappe.cache().ft()
-
-	for item_group in published_item_groups:
-		payload = json.dumps({"name": item_group.name, "route": item_group.route})
-		ac.sugadd(
-			WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE,
-			Suggestion(
-				string=item_group.name,
-				score=frappe.utils.flt(item_group.weightage) or 1.0,
-				payload=payload,  # additional info that can be retrieved later
-			),
-		)
-
-
-@if_redisearch_enabled
-def reindex_all_web_items():
-	items = frappe.get_all("Website Item", fields=get_fields_indexed(), filters={"published": True})
-
-	cache = frappe.cache()
-	for item in items:
-		web_item = create_web_item_map(item)
-		key = make_key(get_cache_key(item.name))
-
-		for field, value in web_item.items():
-			super(RedisWrapper, cache).hset(key, field, value)
-
-
-def get_cache_key(name):
-	name = frappe.scrub(name)
-	return f"{WEBSITE_ITEM_KEY_PREFIX}{name}"
-
-
-def get_fields_indexed():
-	fields_to_index = frappe.db.get_single_value("E Commerce Settings", "search_index_fields")
-	fields_to_index = fields_to_index.split(",") if fields_to_index else []
-
-	mandatory_fields = ["name", "web_item_name", "route", "thumbnail", "ranking"]
-	fields_to_index = fields_to_index + mandatory_fields
-
-	return fields_to_index
-
-
-def raise_redisearch_error():
-	"Create an Error Log and raise error."
-	log = frappe.log_error("Redisearch Error")
-	log_link = frappe.utils.get_link_to_form("Error Log", log.name)
-
-	frappe.throw(
-		msg=_("Something went wrong. Check {0}").format(log_link), title=_("Redisearch Error")
-	)
diff --git a/erpnext/e_commerce/shopping_cart/__init__.py b/erpnext/e_commerce/shopping_cart/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/e_commerce/shopping_cart/__init__.py
+++ /dev/null
diff --git a/erpnext/e_commerce/shopping_cart/cart.py b/erpnext/e_commerce/shopping_cart/cart.py
deleted file mode 100644
index 7c7e169..0000000
--- a/erpnext/e_commerce/shopping_cart/cart.py
+++ /dev/null
@@ -1,721 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-import frappe.defaults
-from frappe import _, throw
-from frappe.contacts.doctype.address.address import get_address_display
-from frappe.contacts.doctype.contact.contact import get_contact_name
-from frappe.utils import cint, cstr, flt, get_fullname
-from frappe.utils.nestedset import get_root_of
-
-from erpnext.accounts.utils import get_account_name
-from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import (
-	get_shopping_cart_settings,
-)
-from erpnext.utilities.product import get_web_item_qty_in_stock
-
-
-class WebsitePriceListMissingError(frappe.ValidationError):
-	pass
-
-
-def set_cart_count(quotation=None):
-	if cint(frappe.db.get_singles_value("E Commerce Settings", "enabled")):
-		if not quotation:
-			quotation = _get_cart_quotation()
-		cart_count = cstr(cint(quotation.get("total_qty")))
-
-		if hasattr(frappe.local, "cookie_manager"):
-			frappe.local.cookie_manager.set_cookie("cart_count", cart_count)
-
-
-@frappe.whitelist()
-def get_cart_quotation(doc=None):
-	party = get_party()
-
-	if not doc:
-		quotation = _get_cart_quotation(party)
-		doc = quotation
-		set_cart_count(quotation)
-
-	addresses = get_address_docs(party=party)
-
-	if not doc.customer_address and addresses:
-		update_cart_address("billing", addresses[0].name)
-
-	return {
-		"doc": decorate_quotation_doc(doc),
-		"shipping_addresses": get_shipping_addresses(party),
-		"billing_addresses": get_billing_addresses(party),
-		"shipping_rules": get_applicable_shipping_rules(party),
-		"cart_settings": frappe.get_cached_doc("E Commerce Settings"),
-	}
-
-
-@frappe.whitelist()
-def get_shipping_addresses(party=None):
-	if not party:
-		party = get_party()
-	addresses = get_address_docs(party=party)
-	return [
-		{"name": address.name, "title": address.address_title, "display": address.display}
-		for address in addresses
-		if address.address_type == "Shipping"
-	]
-
-
-@frappe.whitelist()
-def get_billing_addresses(party=None):
-	if not party:
-		party = get_party()
-	addresses = get_address_docs(party=party)
-	return [
-		{"name": address.name, "title": address.address_title, "display": address.display}
-		for address in addresses
-		if address.address_type == "Billing"
-	]
-
-
-@frappe.whitelist()
-def place_order():
-	quotation = _get_cart_quotation()
-	cart_settings = frappe.db.get_value(
-		"E Commerce Settings", None, ["company", "allow_items_not_in_stock"], as_dict=1
-	)
-	quotation.company = cart_settings.company
-
-	quotation.flags.ignore_permissions = True
-	quotation.submit()
-
-	if quotation.quotation_to == "Lead" and quotation.party_name:
-		# company used to create customer accounts
-		frappe.defaults.set_user_default("company", quotation.company)
-
-	if not (quotation.shipping_address_name or quotation.customer_address):
-		frappe.throw(_("Set Shipping Address or Billing Address"))
-
-	from erpnext.selling.doctype.quotation.quotation import _make_sales_order
-
-	sales_order = frappe.get_doc(_make_sales_order(quotation.name, ignore_permissions=True))
-	sales_order.payment_schedule = []
-
-	if not cint(cart_settings.allow_items_not_in_stock):
-		for item in sales_order.get("items"):
-			item.warehouse = frappe.db.get_value(
-				"Website Item", {"item_code": item.item_code}, "website_warehouse"
-			)
-			is_stock_item = frappe.db.get_value("Item", item.item_code, "is_stock_item")
-
-			if is_stock_item:
-				item_stock = get_web_item_qty_in_stock(item.item_code, "website_warehouse")
-				if not cint(item_stock.in_stock):
-					throw(_("{0} Not in Stock").format(item.item_code))
-				if item.qty > item_stock.stock_qty:
-					throw(_("Only {0} in Stock for item {1}").format(item_stock.stock_qty, item.item_code))
-
-	sales_order.flags.ignore_permissions = True
-	sales_order.insert()
-	sales_order.submit()
-
-	if hasattr(frappe.local, "cookie_manager"):
-		frappe.local.cookie_manager.delete_cookie("cart_count")
-
-	return sales_order.name
-
-
-@frappe.whitelist()
-def request_for_quotation():
-	quotation = _get_cart_quotation()
-	quotation.flags.ignore_permissions = True
-
-	if get_shopping_cart_settings().save_quotations_as_draft:
-		quotation.save()
-	else:
-		quotation.submit()
-	return quotation.name
-
-
-@frappe.whitelist()
-def update_cart(item_code, qty, additional_notes=None, with_items=False):
-	quotation = _get_cart_quotation()
-
-	empty_card = False
-	qty = flt(qty)
-	if qty == 0:
-		quotation_items = quotation.get("items", {"item_code": ["!=", item_code]})
-		if quotation_items:
-			quotation.set("items", quotation_items)
-		else:
-			empty_card = True
-
-	else:
-		warehouse = frappe.get_cached_value(
-			"Website Item", {"item_code": item_code}, "website_warehouse"
-		)
-
-		quotation_items = quotation.get("items", {"item_code": item_code})
-		if not quotation_items:
-			quotation.append(
-				"items",
-				{
-					"doctype": "Quotation Item",
-					"item_code": item_code,
-					"qty": qty,
-					"additional_notes": additional_notes,
-					"warehouse": warehouse,
-				},
-			)
-		else:
-			quotation_items[0].qty = qty
-			quotation_items[0].additional_notes = additional_notes
-			quotation_items[0].warehouse = warehouse
-
-	apply_cart_settings(quotation=quotation)
-
-	quotation.flags.ignore_permissions = True
-	quotation.payment_schedule = []
-	if not empty_card:
-		quotation.save()
-	else:
-		quotation.delete()
-		quotation = None
-
-	set_cart_count(quotation)
-
-	if cint(with_items):
-		context = get_cart_quotation(quotation)
-		return {
-			"items": frappe.render_template("templates/includes/cart/cart_items.html", context),
-			"total": frappe.render_template("templates/includes/cart/cart_items_total.html", context),
-			"taxes_and_totals": frappe.render_template(
-				"templates/includes/cart/cart_payment_summary.html", context
-			),
-		}
-	else:
-		return {"name": quotation.name}
-
-
-@frappe.whitelist()
-def get_shopping_cart_menu(context=None):
-	if not context:
-		context = get_cart_quotation()
-
-	return frappe.render_template("templates/includes/cart/cart_dropdown.html", context)
-
-
-@frappe.whitelist()
-def add_new_address(doc):
-	doc = frappe.parse_json(doc)
-	doc.update({"doctype": "Address"})
-	address = frappe.get_doc(doc)
-	address.save(ignore_permissions=True)
-
-	return address
-
-
-@frappe.whitelist(allow_guest=True)
-def create_lead_for_item_inquiry(lead, subject, message):
-	lead = frappe.parse_json(lead)
-	lead_doc = frappe.new_doc("Lead")
-	for fieldname in ("lead_name", "company_name", "email_id", "phone"):
-		lead_doc.set(fieldname, lead.get(fieldname))
-
-	lead_doc.set("lead_owner", "")
-
-	if not frappe.db.exists("Lead Source", "Product Inquiry"):
-		frappe.get_doc({"doctype": "Lead Source", "source_name": "Product Inquiry"}).insert(
-			ignore_permissions=True
-		)
-
-	lead_doc.set("source", "Product Inquiry")
-
-	try:
-		lead_doc.save(ignore_permissions=True)
-	except frappe.exceptions.DuplicateEntryError:
-		frappe.clear_messages()
-		lead_doc = frappe.get_doc("Lead", {"email_id": lead["email_id"]})
-
-	lead_doc.add_comment(
-		"Comment",
-		text="""
-		<div>
-			<h5>{subject}</h5>
-			<p>{message}</p>
-		</div>
-	""".format(
-			subject=subject, message=message
-		),
-	)
-
-	return lead_doc
-
-
-@frappe.whitelist()
-def get_terms_and_conditions(terms_name):
-	return frappe.db.get_value("Terms and Conditions", terms_name, "terms")
-
-
-@frappe.whitelist()
-def update_cart_address(address_type, address_name):
-	quotation = _get_cart_quotation()
-	address_doc = frappe.get_doc("Address", address_name).as_dict()
-	address_display = get_address_display(address_doc)
-
-	if address_type.lower() == "billing":
-		quotation.customer_address = address_name
-		quotation.address_display = address_display
-		quotation.shipping_address_name = quotation.shipping_address_name or address_name
-		address_doc = next((doc for doc in get_billing_addresses() if doc["name"] == address_name), None)
-	elif address_type.lower() == "shipping":
-		quotation.shipping_address_name = address_name
-		quotation.shipping_address = address_display
-		quotation.customer_address = quotation.customer_address or address_name
-		address_doc = next(
-			(doc for doc in get_shipping_addresses() if doc["name"] == address_name), None
-		)
-	apply_cart_settings(quotation=quotation)
-
-	quotation.flags.ignore_permissions = True
-	quotation.save()
-
-	context = get_cart_quotation(quotation)
-	context["address"] = address_doc
-
-	return {
-		"taxes": frappe.render_template("templates/includes/order/order_taxes.html", context),
-		"address": frappe.render_template("templates/includes/cart/address_card.html", context),
-	}
-
-
-def guess_territory():
-	territory = None
-	geoip_country = frappe.session.get("session_country")
-	if geoip_country:
-		territory = frappe.db.get_value("Territory", geoip_country)
-
-	return (
-		territory
-		or frappe.db.get_value("E Commerce Settings", None, "territory")
-		or get_root_of("Territory")
-	)
-
-
-def decorate_quotation_doc(doc):
-	for d in doc.get("items", []):
-		item_code = d.item_code
-		fields = ["web_item_name", "thumbnail", "website_image", "description", "route"]
-
-		# Variant Item
-		if not frappe.db.exists("Website Item", {"item_code": item_code}):
-			variant_data = frappe.db.get_values(
-				"Item",
-				filters={"item_code": item_code},
-				fieldname=["variant_of", "item_name", "image"],
-				as_dict=True,
-			)[0]
-			item_code = variant_data.variant_of
-			fields = fields[1:]
-			d.web_item_name = variant_data.item_name
-
-			if variant_data.image:  # get image from variant or template web item
-				d.thumbnail = variant_data.image
-				fields = fields[2:]
-
-		d.update(frappe.db.get_value("Website Item", {"item_code": item_code}, fields, as_dict=True))
-		website_warehouse = frappe.get_cached_value(
-			"Website Item", {"item_code": item_code}, "website_warehouse"
-		)
-		d.warehouse = website_warehouse
-
-	return doc
-
-
-def _get_cart_quotation(party=None):
-	"""Return the open Quotation of type "Shopping Cart" or make a new one"""
-	if not party:
-		party = get_party()
-
-	quotation = frappe.get_all(
-		"Quotation",
-		fields=["name"],
-		filters={
-			"party_name": party.name,
-			"contact_email": frappe.session.user,
-			"order_type": "Shopping Cart",
-			"docstatus": 0,
-		},
-		order_by="modified desc",
-		limit_page_length=1,
-	)
-
-	if quotation:
-		qdoc = frappe.get_doc("Quotation", quotation[0].name)
-	else:
-		company = frappe.db.get_value("E Commerce Settings", None, ["company"])
-		qdoc = frappe.get_doc(
-			{
-				"doctype": "Quotation",
-				"naming_series": get_shopping_cart_settings().quotation_series or "QTN-CART-",
-				"quotation_to": party.doctype,
-				"company": company,
-				"order_type": "Shopping Cart",
-				"status": "Draft",
-				"docstatus": 0,
-				"__islocal": 1,
-				"party_name": party.name,
-			}
-		)
-
-		qdoc.contact_person = frappe.db.get_value("Contact", {"email_id": frappe.session.user})
-		qdoc.contact_email = frappe.session.user
-
-		qdoc.flags.ignore_permissions = True
-		qdoc.run_method("set_missing_values")
-		apply_cart_settings(party, qdoc)
-
-	return qdoc
-
-
-def update_party(fullname, company_name=None, mobile_no=None, phone=None):
-	party = get_party()
-
-	party.customer_name = company_name or fullname
-	party.customer_type = "Company" if company_name else "Individual"
-
-	contact_name = frappe.db.get_value("Contact", {"email_id": frappe.session.user})
-	contact = frappe.get_doc("Contact", contact_name)
-	contact.first_name = fullname
-	contact.last_name = None
-	contact.customer_name = party.customer_name
-	contact.mobile_no = mobile_no
-	contact.phone = phone
-	contact.flags.ignore_permissions = True
-	contact.save()
-
-	party_doc = frappe.get_doc(party.as_dict())
-	party_doc.flags.ignore_permissions = True
-	party_doc.save()
-
-	qdoc = _get_cart_quotation(party)
-	if not qdoc.get("__islocal"):
-		qdoc.customer_name = company_name or fullname
-		qdoc.run_method("set_missing_lead_customer_details")
-		qdoc.flags.ignore_permissions = True
-		qdoc.save()
-
-
-def apply_cart_settings(party=None, quotation=None):
-	if not party:
-		party = get_party()
-	if not quotation:
-		quotation = _get_cart_quotation(party)
-
-	cart_settings = frappe.get_doc("E Commerce Settings")
-
-	set_price_list_and_rate(quotation, cart_settings)
-
-	quotation.run_method("calculate_taxes_and_totals")
-
-	set_taxes(quotation, cart_settings)
-
-	_apply_shipping_rule(party, quotation, cart_settings)
-
-
-def set_price_list_and_rate(quotation, cart_settings):
-	"""set price list based on billing territory"""
-
-	_set_price_list(cart_settings, quotation)
-
-	# reset values
-	quotation.price_list_currency = (
-		quotation.currency
-	) = quotation.plc_conversion_rate = quotation.conversion_rate = None
-	for item in quotation.get("items"):
-		item.price_list_rate = item.discount_percentage = item.rate = item.amount = None
-
-	# refetch values
-	quotation.run_method("set_price_list_and_item_details")
-
-	if hasattr(frappe.local, "cookie_manager"):
-		# set it in cookies for using in product page
-		frappe.local.cookie_manager.set_cookie("selling_price_list", quotation.selling_price_list)
-
-
-def _set_price_list(cart_settings, quotation=None):
-	"""Set price list based on customer or shopping cart default"""
-	from erpnext.accounts.party import get_default_price_list
-
-	party_name = quotation.get("party_name") if quotation else get_party().get("name")
-	selling_price_list = None
-
-	# check if default customer price list exists
-	if party_name and frappe.db.exists("Customer", party_name):
-		selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name))
-
-	# check default price list in shopping cart
-	if not selling_price_list:
-		selling_price_list = cart_settings.price_list
-
-	if quotation:
-		quotation.selling_price_list = selling_price_list
-
-	return selling_price_list
-
-
-def set_taxes(quotation, cart_settings):
-	"""set taxes based on billing territory"""
-	from erpnext.accounts.party import set_taxes
-
-	customer_group = frappe.db.get_value("Customer", quotation.party_name, "customer_group")
-
-	quotation.taxes_and_charges = set_taxes(
-		quotation.party_name,
-		"Customer",
-		quotation.transaction_date,
-		quotation.company,
-		customer_group=customer_group,
-		supplier_group=None,
-		tax_category=quotation.tax_category,
-		billing_address=quotation.customer_address,
-		shipping_address=quotation.shipping_address_name,
-		use_for_shopping_cart=1,
-	)
-	#
-	# 	# clear table
-	quotation.set("taxes", [])
-	#
-	# 	# append taxes
-	quotation.append_taxes_from_master()
-
-
-def get_party(user=None):
-	if not user:
-		user = frappe.session.user
-
-	contact_name = get_contact_name(user)
-	party = None
-
-	if contact_name:
-		contact = frappe.get_doc("Contact", contact_name)
-		if contact.links:
-			party_doctype = contact.links[0].link_doctype
-			party = contact.links[0].link_name
-
-	cart_settings = frappe.get_doc("E Commerce Settings")
-
-	debtors_account = ""
-
-	if cart_settings.enable_checkout:
-		debtors_account = get_debtors_account(cart_settings)
-
-	if party:
-		return frappe.get_doc(party_doctype, party)
-
-	else:
-		if not cart_settings.enabled:
-			frappe.local.flags.redirect_location = "/contact"
-			raise frappe.Redirect
-		customer = frappe.new_doc("Customer")
-		fullname = get_fullname(user)
-		customer.update(
-			{
-				"customer_name": fullname,
-				"customer_type": "Individual",
-				"customer_group": get_shopping_cart_settings().default_customer_group,
-				"territory": get_root_of("Territory"),
-			}
-		)
-
-		customer.append("portal_users", {"user": user})
-
-		if debtors_account:
-			customer.update({"accounts": [{"company": cart_settings.company, "account": debtors_account}]})
-
-		customer.flags.ignore_mandatory = True
-		customer.insert(ignore_permissions=True)
-
-		contact = frappe.new_doc("Contact")
-		contact.update({"first_name": fullname, "email_ids": [{"email_id": user, "is_primary": 1}]})
-		contact.append("links", dict(link_doctype="Customer", link_name=customer.name))
-		contact.flags.ignore_mandatory = True
-		contact.insert(ignore_permissions=True)
-
-		return customer
-
-
-def get_debtors_account(cart_settings):
-	if not cart_settings.payment_gateway_account:
-		frappe.throw(_("Payment Gateway Account not set"), _("Mandatory"))
-
-	payment_gateway_account_currency = frappe.get_doc(
-		"Payment Gateway Account", cart_settings.payment_gateway_account
-	).currency
-
-	account_name = _("Debtors ({0})").format(payment_gateway_account_currency)
-
-	debtors_account_name = get_account_name(
-		"Receivable",
-		"Asset",
-		is_group=0,
-		account_currency=payment_gateway_account_currency,
-		company=cart_settings.company,
-	)
-
-	if not debtors_account_name:
-		debtors_account = frappe.get_doc(
-			{
-				"doctype": "Account",
-				"account_type": "Receivable",
-				"root_type": "Asset",
-				"is_group": 0,
-				"parent_account": get_account_name(
-					root_type="Asset", is_group=1, company=cart_settings.company
-				),
-				"account_name": account_name,
-				"currency": payment_gateway_account_currency,
-			}
-		).insert(ignore_permissions=True)
-
-		return debtors_account.name
-
-	else:
-		return debtors_account_name
-
-
-def get_address_docs(
-	doctype=None, txt=None, filters=None, limit_start=0, limit_page_length=20, party=None
-):
-	if not party:
-		party = get_party()
-
-	if not party:
-		return []
-
-	address_names = frappe.db.get_all(
-		"Dynamic Link",
-		fields=("parent"),
-		filters=dict(parenttype="Address", link_doctype=party.doctype, link_name=party.name),
-	)
-
-	out = []
-
-	for a in address_names:
-		address = frappe.get_doc("Address", a.parent)
-		address.display = get_address_display(address.as_dict())
-		out.append(address)
-
-	return out
-
-
-@frappe.whitelist()
-def apply_shipping_rule(shipping_rule):
-	quotation = _get_cart_quotation()
-
-	quotation.shipping_rule = shipping_rule
-
-	apply_cart_settings(quotation=quotation)
-
-	quotation.flags.ignore_permissions = True
-	quotation.save()
-
-	return get_cart_quotation(quotation)
-
-
-def _apply_shipping_rule(party=None, quotation=None, cart_settings=None):
-	if not quotation.shipping_rule:
-		shipping_rules = get_shipping_rules(quotation, cart_settings)
-
-		if not shipping_rules:
-			return
-
-		elif quotation.shipping_rule not in shipping_rules:
-			quotation.shipping_rule = shipping_rules[0]
-
-	if quotation.shipping_rule:
-		quotation.run_method("apply_shipping_rule")
-		quotation.run_method("calculate_taxes_and_totals")
-
-
-def get_applicable_shipping_rules(party=None, quotation=None):
-	shipping_rules = get_shipping_rules(quotation)
-
-	if shipping_rules:
-		# we need this in sorted order as per the position of the rule in the settings page
-		return [[rule, rule] for rule in shipping_rules]
-
-
-def get_shipping_rules(quotation=None, cart_settings=None):
-	if not quotation:
-		quotation = _get_cart_quotation()
-
-	shipping_rules = []
-	if quotation.shipping_address_name:
-		country = frappe.db.get_value("Address", quotation.shipping_address_name, "country")
-		if country:
-			sr_country = frappe.qb.DocType("Shipping Rule Country")
-			sr = frappe.qb.DocType("Shipping Rule")
-			query = (
-				frappe.qb.from_(sr_country)
-				.join(sr)
-				.on(sr.name == sr_country.parent)
-				.select(sr.name)
-				.distinct()
-				.where((sr_country.country == country) & (sr.disabled != 1))
-			)
-			result = query.run(as_list=True)
-			shipping_rules = [x[0] for x in result]
-
-	return shipping_rules
-
-
-def get_address_territory(address_name):
-	"""Tries to match city, state and country of address to existing territory"""
-	territory = None
-
-	if address_name:
-		address_fields = frappe.db.get_value("Address", address_name, ["city", "state", "country"])
-		for value in address_fields:
-			territory = frappe.db.get_value("Territory", value)
-			if territory:
-				break
-
-	return territory
-
-
-def show_terms(doc):
-	return doc.tc_name
-
-
-@frappe.whitelist(allow_guest=True)
-def apply_coupon_code(applied_code, applied_referral_sales_partner):
-	quotation = True
-
-	if not applied_code:
-		frappe.throw(_("Please enter a coupon code"))
-
-	coupon_list = frappe.get_all("Coupon Code", filters={"coupon_code": applied_code})
-	if not coupon_list:
-		frappe.throw(_("Please enter a valid coupon code"))
-
-	coupon_name = coupon_list[0].name
-
-	from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code
-
-	validate_coupon_code(coupon_name)
-	quotation = _get_cart_quotation()
-	quotation.coupon_code = coupon_name
-	quotation.flags.ignore_permissions = True
-	quotation.save()
-
-	if applied_referral_sales_partner:
-		sales_partner_list = frappe.get_all(
-			"Sales Partner", filters={"referral_code": applied_referral_sales_partner}
-		)
-		if sales_partner_list:
-			sales_partner_name = sales_partner_list[0].name
-			quotation.referral_sales_partner = sales_partner_name
-			quotation.flags.ignore_permissions = True
-			quotation.save()
-
-	return quotation
diff --git a/erpnext/e_commerce/shopping_cart/product_info.py b/erpnext/e_commerce/shopping_cart/product_info.py
deleted file mode 100644
index 0248ca7..0000000
--- a/erpnext/e_commerce/shopping_cart/product_info.py
+++ /dev/null
@@ -1,99 +0,0 @@
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-
-from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import (
-	get_shopping_cart_settings,
-	show_quantity_in_website,
-)
-from erpnext.e_commerce.shopping_cart.cart import _get_cart_quotation, _set_price_list
-from erpnext.utilities.product import (
-	get_non_stock_item_status,
-	get_price,
-	get_web_item_qty_in_stock,
-)
-
-
-@frappe.whitelist(allow_guest=True)
-def get_product_info_for_website(item_code, skip_quotation_creation=False):
-	"""get product price / stock info for website"""
-
-	cart_settings = get_shopping_cart_settings()
-	if not cart_settings.enabled:
-		# return settings even if cart is disabled
-		return frappe._dict({"product_info": {}, "cart_settings": cart_settings})
-
-	cart_quotation = frappe._dict()
-	if not skip_quotation_creation:
-		cart_quotation = _get_cart_quotation()
-
-	selling_price_list = (
-		cart_quotation.get("selling_price_list")
-		if cart_quotation
-		else _set_price_list(cart_settings, None)
-	)
-
-	price = {}
-	if cart_settings.show_price:
-		is_guest = frappe.session.user == "Guest"
-		# Show Price if logged in.
-		# If not logged in, check if price is hidden for guest.
-		if not is_guest or not cart_settings.hide_price_for_guest:
-			price = get_price(
-				item_code, selling_price_list, cart_settings.default_customer_group, cart_settings.company
-			)
-
-	stock_status = None
-
-	if cart_settings.show_stock_availability:
-		on_backorder = frappe.get_cached_value("Website Item", {"item_code": item_code}, "on_backorder")
-		if on_backorder:
-			stock_status = frappe._dict({"on_backorder": True})
-		else:
-			stock_status = get_web_item_qty_in_stock(item_code, "website_warehouse")
-
-	product_info = {
-		"price": price,
-		"qty": 0,
-		"uom": frappe.db.get_value("Item", item_code, "stock_uom"),
-		"sales_uom": frappe.db.get_value("Item", item_code, "sales_uom"),
-	}
-
-	if stock_status:
-		if stock_status.on_backorder:
-			product_info["on_backorder"] = True
-		else:
-			product_info["stock_qty"] = stock_status.stock_qty
-			product_info["in_stock"] = (
-				stock_status.in_stock
-				if stock_status.is_stock_item
-				else get_non_stock_item_status(item_code, "website_warehouse")
-			)
-			product_info["show_stock_qty"] = show_quantity_in_website()
-
-	if product_info["price"]:
-		if frappe.session.user != "Guest":
-			item = cart_quotation.get({"item_code": item_code}) if cart_quotation else None
-			if item:
-				product_info["qty"] = item[0].qty
-
-	return frappe._dict({"product_info": product_info, "cart_settings": cart_settings})
-
-
-def set_product_info_for_website(item):
-	"""set product price uom for website"""
-	product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get(
-		"product_info"
-	)
-
-	if product_info:
-		item.update(product_info)
-		item["stock_uom"] = product_info.get("uom")
-		item["sales_uom"] = product_info.get("sales_uom")
-		if product_info.get("price"):
-			item["price_stock_uom"] = product_info.get("price").get("formatted_price")
-			item["price_sales_uom"] = product_info.get("price").get("formatted_price_sales_uom")
-		else:
-			item["price_stock_uom"] = ""
-			item["price_sales_uom"] = ""
diff --git a/erpnext/e_commerce/shopping_cart/test_shopping_cart.py b/erpnext/e_commerce/shopping_cart/test_shopping_cart.py
deleted file mode 100644
index 8210f97..0000000
--- a/erpnext/e_commerce/shopping_cart/test_shopping_cart.py
+++ /dev/null
@@ -1,398 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-import unittest
-
-import frappe
-from frappe.tests.utils import change_settings
-from frappe.utils import add_months, cint, nowdate
-
-from erpnext.accounts.doctype.tax_rule.tax_rule import ConflictingTaxRule
-from erpnext.e_commerce.doctype.website_item.website_item import make_website_item
-from erpnext.e_commerce.shopping_cart.cart import (
-	_get_cart_quotation,
-	get_cart_quotation,
-	get_party,
-	request_for_quotation,
-	update_cart,
-)
-
-
-class TestShoppingCart(unittest.TestCase):
-	"""
-	Note:
-	Shopping Cart == Quotation
-	"""
-
-	def setUp(self):
-		frappe.set_user("Administrator")
-		self.enable_shopping_cart()
-		if not frappe.db.exists("Website Item", {"item_code": "_Test Item"}):
-			make_website_item(frappe.get_cached_doc("Item", "_Test Item"))
-
-		if not frappe.db.exists("Website Item", {"item_code": "_Test Item 2"}):
-			make_website_item(frappe.get_cached_doc("Item", "_Test Item 2"))
-
-	def tearDown(self):
-		frappe.db.rollback()
-		frappe.set_user("Administrator")
-		self.disable_shopping_cart()
-
-	@classmethod
-	def tearDownClass(cls):
-		frappe.db.sql("delete from `tabTax Rule`")
-
-	def test_get_cart_new_user(self):
-		self.login_as_customer(
-			"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
-		)
-		create_address_and_contact(
-			address_title="_Test Address for Customer 2",
-			first_name="_Test Contact for Customer 2",
-			email="test_contact_two_customer@example.com",
-			customer="_Test Customer 2",
-		)
-		# test if lead is created and quotation with new lead is fetched
-		customer = frappe.get_doc("Customer", "_Test Customer 2")
-		quotation = _get_cart_quotation(party=customer)
-		self.assertEqual(quotation.quotation_to, "Customer")
-		self.assertEqual(
-			quotation.contact_person,
-			frappe.db.get_value("Contact", dict(email_id="test_contact_two_customer@example.com")),
-		)
-		self.assertEqual(quotation.contact_email, frappe.session.user)
-
-		return quotation
-
-	def test_get_cart_customer(self, customer="_Test Customer 2"):
-		def validate_quotation(customer_name):
-			# test if quotation with customer is fetched
-			party = frappe.get_doc("Customer", customer_name)
-			quotation = _get_cart_quotation(party=party)
-			self.assertEqual(quotation.quotation_to, "Customer")
-			self.assertEqual(quotation.party_name, customer_name)
-			self.assertEqual(quotation.contact_email, frappe.session.user)
-			return quotation
-
-		quotation = validate_quotation(customer)
-		return quotation
-
-	def test_add_to_cart(self):
-		self.login_as_customer(
-			"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
-		)
-		create_address_and_contact(
-			address_title="_Test Address for Customer 2",
-			first_name="_Test Contact for Customer 2",
-			email="test_contact_two_customer@example.com",
-			customer="_Test Customer 2",
-		)
-		# clear existing quotations
-		self.clear_existing_quotations()
-
-		# add first item
-		update_cart("_Test Item", 1)
-
-		quotation = self.test_get_cart_customer("_Test Customer 2")
-
-		self.assertEqual(quotation.get("items")[0].item_code, "_Test Item")
-		self.assertEqual(quotation.get("items")[0].qty, 1)
-		self.assertEqual(quotation.get("items")[0].amount, 10)
-
-		# add second item
-		update_cart("_Test Item 2", 1)
-		quotation = self.test_get_cart_customer("_Test Customer 2")
-		self.assertEqual(quotation.get("items")[1].item_code, "_Test Item 2")
-		self.assertEqual(quotation.get("items")[1].qty, 1)
-		self.assertEqual(quotation.get("items")[1].amount, 20)
-
-		self.assertEqual(len(quotation.get("items")), 2)
-
-	def test_update_cart(self):
-		# first, add to cart
-		self.test_add_to_cart()
-
-		# update first item
-		update_cart("_Test Item", 5)
-		quotation = self.test_get_cart_customer("_Test Customer 2")
-		self.assertEqual(quotation.get("items")[0].item_code, "_Test Item")
-		self.assertEqual(quotation.get("items")[0].qty, 5)
-		self.assertEqual(quotation.get("items")[0].amount, 50)
-		self.assertEqual(quotation.net_total, 70)
-		self.assertEqual(len(quotation.get("items")), 2)
-
-	def test_remove_from_cart(self):
-		# first, add to cart
-		self.test_add_to_cart()
-
-		# remove first item
-		update_cart("_Test Item", 0)
-		quotation = self.test_get_cart_customer("_Test Customer 2")
-
-		self.assertEqual(quotation.get("items")[0].item_code, "_Test Item 2")
-		self.assertEqual(quotation.get("items")[0].qty, 1)
-		self.assertEqual(quotation.get("items")[0].amount, 20)
-		self.assertEqual(quotation.net_total, 20)
-		self.assertEqual(len(quotation.get("items")), 1)
-
-	@unittest.skip("Flaky in CI")
-	def test_tax_rule(self):
-		self.create_tax_rule()
-
-		self.login_as_customer(
-			"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
-		)
-		create_address_and_contact(
-			address_title="_Test Address for Customer 2",
-			first_name="_Test Contact for Customer 2",
-			email="test_contact_two_customer@example.com",
-			customer="_Test Customer 2",
-		)
-
-		quotation = self.create_quotation()
-
-		from erpnext.accounts.party import set_taxes
-
-		tax_rule_master = set_taxes(
-			quotation.party_name,
-			"Customer",
-			None,
-			quotation.company,
-			customer_group=None,
-			supplier_group=None,
-			tax_category=quotation.tax_category,
-			billing_address=quotation.customer_address,
-			shipping_address=quotation.shipping_address_name,
-			use_for_shopping_cart=1,
-		)
-
-		self.assertEqual(quotation.taxes_and_charges, tax_rule_master)
-		self.assertEqual(quotation.total_taxes_and_charges, 1000.0)
-
-		self.remove_test_quotation(quotation)
-
-	@change_settings(
-		"E Commerce Settings",
-		{
-			"company": "_Test Company",
-			"enabled": 1,
-			"default_customer_group": "_Test Customer Group",
-			"price_list": "_Test Price List India",
-			"show_price": 1,
-		},
-	)
-	def test_add_item_variant_without_web_item_to_cart(self):
-		"Test adding Variants having no Website Items in cart via Template Web Item."
-		from erpnext.controllers.item_variant import create_variant
-		from erpnext.e_commerce.doctype.website_item.website_item import make_website_item
-		from erpnext.stock.doctype.item.test_item import make_item
-
-		template_item = make_item(
-			"Test-Tshirt-Temp",
-			{
-				"has_variant": 1,
-				"variant_based_on": "Item Attribute",
-				"attributes": [{"attribute": "Test Size"}, {"attribute": "Test Colour"}],
-			},
-		)
-		variant = create_variant("Test-Tshirt-Temp", {"Test Size": "Small", "Test Colour": "Red"})
-		variant.save()
-		make_website_item(template_item)  # publish template not variant
-
-		update_cart("Test-Tshirt-Temp-S-R", 1)
-
-		cart = get_cart_quotation()  # test if cart page gets data without errors
-		doc = cart.get("doc")
-
-		self.assertEqual(doc.get("items")[0].item_name, "Test-Tshirt-Temp-S-R")
-
-		# test if items are rendered without error
-		frappe.render_template("templates/includes/cart/cart_items.html", cart)
-
-	@change_settings("E Commerce Settings", {"save_quotations_as_draft": 1})
-	def test_cart_without_checkout_and_draft_quotation(self):
-		"Test impact of 'save_quotations_as_draft' checkbox."
-		frappe.local.shopping_cart_settings = None
-
-		# add item to cart
-		update_cart("_Test Item", 1)
-		quote_name = request_for_quotation()  # Request for Quote
-		quote_doctstatus = cint(frappe.db.get_value("Quotation", quote_name, "docstatus"))
-
-		self.assertEqual(quote_doctstatus, 0)
-
-		frappe.db.set_single_value("E Commerce Settings", "save_quotations_as_draft", 0)
-		frappe.local.shopping_cart_settings = None
-		update_cart("_Test Item", 1)
-		quote_name = request_for_quotation()  # Request for Quote
-		quote_doctstatus = cint(frappe.db.get_value("Quotation", quote_name, "docstatus"))
-
-		self.assertEqual(quote_doctstatus, 1)
-
-	def create_tax_rule(self):
-		tax_rule = frappe.get_test_records("Tax Rule")[0]
-		try:
-			frappe.get_doc(tax_rule).insert(ignore_if_duplicate=True)
-		except (frappe.DuplicateEntryError, ConflictingTaxRule):
-			pass
-
-	def create_quotation(self):
-		quotation = frappe.new_doc("Quotation")
-
-		values = {
-			"doctype": "Quotation",
-			"quotation_to": "Customer",
-			"order_type": "Shopping Cart",
-			"party_name": get_party(frappe.session.user).name,
-			"docstatus": 0,
-			"contact_email": frappe.session.user,
-			"selling_price_list": "_Test Price List Rest of the World",
-			"currency": "USD",
-			"taxes_and_charges": "_Test Tax 1 - _TC",
-			"conversion_rate": 1,
-			"transaction_date": nowdate(),
-			"valid_till": add_months(nowdate(), 1),
-			"items": [{"item_code": "_Test Item", "qty": 1}],
-			"taxes": frappe.get_doc("Sales Taxes and Charges Template", "_Test Tax 1 - _TC").taxes,
-			"company": "_Test Company",
-		}
-
-		quotation.update(values)
-
-		quotation.insert(ignore_permissions=True)
-
-		return quotation
-
-	def remove_test_quotation(self, quotation):
-		frappe.set_user("Administrator")
-		quotation.delete()
-
-	# helper functions
-	def enable_shopping_cart(self):
-		settings = frappe.get_doc("E Commerce Settings", "E Commerce Settings")
-
-		settings.update(
-			{
-				"enabled": 1,
-				"company": "_Test Company",
-				"default_customer_group": "_Test Customer Group",
-				"quotation_series": "_T-Quotation-",
-				"price_list": "_Test Price List India",
-			}
-		)
-
-		# insert item price
-		if not frappe.db.get_value(
-			"Item Price", {"price_list": "_Test Price List India", "item_code": "_Test Item"}
-		):
-			frappe.get_doc(
-				{
-					"doctype": "Item Price",
-					"price_list": "_Test Price List India",
-					"item_code": "_Test Item",
-					"price_list_rate": 10,
-				}
-			).insert()
-			frappe.get_doc(
-				{
-					"doctype": "Item Price",
-					"price_list": "_Test Price List India",
-					"item_code": "_Test Item 2",
-					"price_list_rate": 20,
-				}
-			).insert()
-
-		settings.save()
-		frappe.local.shopping_cart_settings = None
-
-	def disable_shopping_cart(self):
-		settings = frappe.get_doc("E Commerce Settings", "E Commerce Settings")
-		settings.enabled = 0
-		settings.save()
-		frappe.local.shopping_cart_settings = None
-
-	def login_as_new_user(self):
-		self.create_user_if_not_exists("test_cart_user@example.com")
-		frappe.set_user("test_cart_user@example.com")
-
-	def login_as_customer(
-		self, email="test_contact_customer@example.com", name="_Test Contact For _Test Customer"
-	):
-		self.create_user_if_not_exists(email, name)
-		frappe.set_user(email)
-
-	def clear_existing_quotations(self):
-		quotations = frappe.get_all(
-			"Quotation",
-			filters={"party_name": get_party().name, "order_type": "Shopping Cart", "docstatus": 0},
-			order_by="modified desc",
-			pluck="name",
-		)
-
-		for quotation in quotations:
-			frappe.delete_doc("Quotation", quotation, ignore_permissions=True, force=True)
-
-	def create_user_if_not_exists(self, email, first_name=None):
-		if frappe.db.exists("User", email):
-			return
-
-		user = frappe.get_doc(
-			{
-				"doctype": "User",
-				"user_type": "Website User",
-				"email": email,
-				"send_welcome_email": 0,
-				"first_name": first_name or email.split("@")[0],
-			}
-		).insert(ignore_permissions=True)
-
-		user.add_roles("Customer")
-
-
-def create_address_and_contact(**kwargs):
-	if not frappe.db.get_value("Address", {"address_title": kwargs.get("address_title")}):
-		frappe.get_doc(
-			{
-				"doctype": "Address",
-				"address_title": kwargs.get("address_title"),
-				"address_type": kwargs.get("address_type") or "Office",
-				"address_line1": kwargs.get("address_line1") or "Station Road",
-				"city": kwargs.get("city") or "_Test City",
-				"state": kwargs.get("state") or "Test State",
-				"country": kwargs.get("country") or "India",
-				"links": [
-					{"link_doctype": "Customer", "link_name": kwargs.get("customer") or "_Test Customer"}
-				],
-			}
-		).insert()
-
-	if not frappe.db.get_value("Contact", {"first_name": kwargs.get("first_name")}):
-		contact = frappe.get_doc(
-			{
-				"doctype": "Contact",
-				"first_name": kwargs.get("first_name"),
-				"links": [
-					{"link_doctype": "Customer", "link_name": kwargs.get("customer") or "_Test Customer"}
-				],
-			}
-		)
-		contact.add_email(kwargs.get("email") or "test_contact_customer@example.com", is_primary=True)
-		contact.add_phone(kwargs.get("phone") or "+91 0000000000", is_primary_phone=True)
-		contact.insert()
-
-
-test_dependencies = [
-	"Sales Taxes and Charges Template",
-	"Price List",
-	"Item Price",
-	"Shipping Rule",
-	"Currency Exchange",
-	"Customer Group",
-	"Lead",
-	"Customer",
-	"Contact",
-	"Address",
-	"Item",
-	"Tax Rule",
-]
diff --git a/erpnext/e_commerce/shopping_cart/utils.py b/erpnext/e_commerce/shopping_cart/utils.py
deleted file mode 100644
index 3d48c28..0000000
--- a/erpnext/e_commerce/shopping_cart/utils.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-import frappe
-
-from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import is_cart_enabled
-
-
-def show_cart_count():
-	if (
-		is_cart_enabled()
-		and frappe.db.get_value("User", frappe.session.user, "user_type") == "Website User"
-	):
-		return True
-
-	return False
-
-
-def set_cart_count(login_manager):
-	# since this is run only on hooks login event
-	# make sure user is already a customer
-	# before trying to set cart count
-	user_is_customer = is_customer()
-	if not user_is_customer:
-		return
-
-	if show_cart_count():
-		from erpnext.e_commerce.shopping_cart.cart import set_cart_count
-
-		# set_cart_count will try to fetch existing cart quotation
-		# or create one if non existent (and create a customer too)
-		# cart count is calculated from this quotation's items
-		set_cart_count()
-
-
-def clear_cart_count(login_manager):
-	if show_cart_count():
-		frappe.local.cookie_manager.delete_cookie("cart_count")
-
-
-def update_website_context(context):
-	cart_enabled = is_cart_enabled()
-	context["shopping_cart_enabled"] = cart_enabled
-
-
-def is_customer():
-	if frappe.session.user and frappe.session.user != "Guest":
-		contact_name = frappe.get_value("Contact", {"email_id": frappe.session.user})
-		if contact_name:
-			contact = frappe.get_doc("Contact", contact_name)
-			for link in contact.links:
-				if link.link_doctype == "Customer":
-					return True
-
-		return False
diff --git a/erpnext/e_commerce/variant_selector/__init__.py b/erpnext/e_commerce/variant_selector/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/e_commerce/variant_selector/__init__.py
+++ /dev/null
diff --git a/erpnext/e_commerce/variant_selector/item_variants_cache.py b/erpnext/e_commerce/variant_selector/item_variants_cache.py
deleted file mode 100644
index f8439d5..0000000
--- a/erpnext/e_commerce/variant_selector/item_variants_cache.py
+++ /dev/null
@@ -1,130 +0,0 @@
-import frappe
-
-
-class ItemVariantsCacheManager:
-	def __init__(self, item_code):
-		self.item_code = item_code
-
-	def get_item_variants_data(self):
-		val = frappe.cache().hget("item_variants_data", self.item_code)
-
-		if not val:
-			self.build_cache()
-
-		return frappe.cache().hget("item_variants_data", self.item_code)
-
-	def get_attribute_value_item_map(self):
-		val = frappe.cache().hget("attribute_value_item_map", self.item_code)
-
-		if not val:
-			self.build_cache()
-
-		return frappe.cache().hget("attribute_value_item_map", self.item_code)
-
-	def get_item_attribute_value_map(self):
-		val = frappe.cache().hget("item_attribute_value_map", self.item_code)
-
-		if not val:
-			self.build_cache()
-
-		return frappe.cache().hget("item_attribute_value_map", self.item_code)
-
-	def get_optional_attributes(self):
-		val = frappe.cache().hget("optional_attributes", self.item_code)
-
-		if not val:
-			self.build_cache()
-
-		return frappe.cache().hget("optional_attributes", self.item_code)
-
-	def get_ordered_attribute_values(self):
-		val = frappe.cache().get_value("ordered_attribute_values_map")
-		if val:
-			return val
-
-		all_attribute_values = frappe.get_all(
-			"Item Attribute Value", ["attribute_value", "idx", "parent"], order_by="idx asc"
-		)
-
-		ordered_attribute_values_map = frappe._dict({})
-		for d in all_attribute_values:
-			ordered_attribute_values_map.setdefault(d.parent, []).append(d.attribute_value)
-
-		frappe.cache().set_value("ordered_attribute_values_map", ordered_attribute_values_map)
-		return ordered_attribute_values_map
-
-	def build_cache(self):
-		parent_item_code = self.item_code
-
-		attributes = [
-			a.attribute
-			for a in frappe.get_all(
-				"Item Variant Attribute", {"parent": parent_item_code}, ["attribute"], order_by="idx asc"
-			)
-		]
-
-		# Get Variants and tehir Attributes that are not disabled
-		iva = frappe.qb.DocType("Item Variant Attribute")
-		item = frappe.qb.DocType("Item")
-		query = (
-			frappe.qb.from_(iva)
-			.join(item)
-			.on(item.name == iva.parent)
-			.select(iva.parent, iva.attribute, iva.attribute_value)
-			.where((iva.variant_of == parent_item_code) & (item.disabled == 0))
-			.orderby(iva.name)
-		)
-		item_variants_data = query.run()
-
-		attribute_value_item_map = frappe._dict()
-		item_attribute_value_map = frappe._dict()
-
-		for row in item_variants_data:
-			item_code, attribute, attribute_value = row
-			# (attr, value) => [item1, item2]
-			attribute_value_item_map.setdefault((attribute, attribute_value), []).append(item_code)
-			# item => {attr1: value1, attr2: value2}
-			item_attribute_value_map.setdefault(item_code, {})[attribute] = attribute_value
-
-		optional_attributes = set()
-		for item_code, attr_dict in item_attribute_value_map.items():
-			for attribute in attributes:
-				if attribute not in attr_dict:
-					optional_attributes.add(attribute)
-
-		frappe.cache().hset("attribute_value_item_map", parent_item_code, attribute_value_item_map)
-		frappe.cache().hset("item_attribute_value_map", parent_item_code, item_attribute_value_map)
-		frappe.cache().hset("item_variants_data", parent_item_code, item_variants_data)
-		frappe.cache().hset("optional_attributes", parent_item_code, optional_attributes)
-
-	def clear_cache(self):
-		keys = [
-			"attribute_value_item_map",
-			"item_attribute_value_map",
-			"item_variants_data",
-			"optional_attributes",
-		]
-
-		for key in keys:
-			frappe.cache().hdel(key, self.item_code)
-
-	def rebuild_cache(self):
-		self.clear_cache()
-		enqueue_build_cache(self.item_code)
-
-
-def build_cache(item_code):
-	frappe.cache().hset("item_cache_build_in_progress", item_code, 1)
-	i = ItemVariantsCacheManager(item_code)
-	i.build_cache()
-	frappe.cache().hset("item_cache_build_in_progress", item_code, 0)
-
-
-def enqueue_build_cache(item_code):
-	if frappe.cache().hget("item_cache_build_in_progress", item_code):
-		return
-	frappe.enqueue(
-		"erpnext.e_commerce.variant_selector.item_variants_cache.build_cache",
-		item_code=item_code,
-		queue="long",
-	)
diff --git a/erpnext/e_commerce/variant_selector/test_variant_selector.py b/erpnext/e_commerce/variant_selector/test_variant_selector.py
deleted file mode 100644
index 8eb497c..0000000
--- a/erpnext/e_commerce/variant_selector/test_variant_selector.py
+++ /dev/null
@@ -1,125 +0,0 @@
-import frappe
-from frappe.tests.utils import FrappeTestCase
-
-from erpnext.controllers.item_variant import create_variant
-from erpnext.e_commerce.doctype.e_commerce_settings.test_e_commerce_settings import (
-	setup_e_commerce_settings,
-)
-from erpnext.e_commerce.doctype.website_item.website_item import make_website_item
-from erpnext.e_commerce.variant_selector.utils import get_next_attribute_and_values
-from erpnext.stock.doctype.item.test_item import make_item
-
-test_dependencies = ["Item"]
-
-
-class TestVariantSelector(FrappeTestCase):
-	@classmethod
-	def setUpClass(cls):
-		super().setUpClass()
-		template_item = make_item(
-			"Test-Tshirt-Temp",
-			{
-				"has_variant": 1,
-				"variant_based_on": "Item Attribute",
-				"attributes": [{"attribute": "Test Size"}, {"attribute": "Test Colour"}],
-			},
-		)
-
-		# create L-R, L-G, M-R, M-G and S-R
-		for size in (
-			"Large",
-			"Medium",
-		):
-			for colour in (
-				"Red",
-				"Green",
-			):
-				variant = create_variant("Test-Tshirt-Temp", {"Test Size": size, "Test Colour": colour})
-				variant.save()
-
-		variant = create_variant("Test-Tshirt-Temp", {"Test Size": "Small", "Test Colour": "Red"})
-		variant.save()
-
-		make_website_item(template_item)  # publish template not variants
-
-	def test_item_attributes(self):
-		"""
-		Test if the right attributes are fetched in the popup.
-		(Attributes must only come from active items)
-
-		Attribute selection must not be linked to Website Items.
-		"""
-		from erpnext.e_commerce.variant_selector.utils import get_attributes_and_values
-
-		attr_data = get_attributes_and_values("Test-Tshirt-Temp")
-
-		self.assertEqual(attr_data[0]["attribute"], "Test Size")
-		self.assertEqual(attr_data[1]["attribute"], "Test Colour")
-		self.assertEqual(len(attr_data[0]["values"]), 3)  # ['Small', 'Medium', 'Large']
-		self.assertEqual(len(attr_data[1]["values"]), 2)  # ['Red', 'Green']
-
-		# disable small red tshirt, now there are no small tshirts.
-		# but there are some red tshirts
-		small_variant = frappe.get_doc("Item", "Test-Tshirt-Temp-S-R")
-		small_variant.disabled = 1
-		small_variant.save()  # trigger cache rebuild
-
-		attr_data = get_attributes_and_values("Test-Tshirt-Temp")
-
-		# Only L and M attribute values must be fetched since S is disabled
-		self.assertEqual(len(attr_data[0]["values"]), 2)  # ['Medium', 'Large']
-
-		# teardown
-		small_variant.disabled = 0
-		small_variant.save()
-
-	def test_next_item_variant_values(self):
-		"""
-		Test if on selecting an attribute value, the next possible values
-		are filtered accordingly.
-		Values that dont apply should not be fetched.
-		E.g.
-		There is a ** Small-Red ** Tshirt. No other colour in this size.
-		On selecting ** Small **, only ** Red ** should be selectable next.
-		"""
-		next_values = get_next_attribute_and_values(
-			"Test-Tshirt-Temp", selected_attributes={"Test Size": "Small"}
-		)
-		next_colours = next_values["valid_options_for_attributes"]["Test Colour"]
-		filtered_items = next_values["filtered_items"]
-
-		self.assertEqual(len(next_colours), 1)
-		self.assertEqual(next_colours.pop(), "Red")
-		self.assertEqual(len(filtered_items), 1)
-		self.assertEqual(filtered_items.pop(), "Test-Tshirt-Temp-S-R")
-
-	def test_exact_match_with_price(self):
-		"""
-		Test price fetching and matching of variant without Website Item
-		"""
-		from erpnext.e_commerce.doctype.website_item.test_website_item import make_web_item_price
-
-		frappe.set_user("Administrator")
-		setup_e_commerce_settings(
-			{
-				"company": "_Test Company",
-				"enabled": 1,
-				"default_customer_group": "_Test Customer Group",
-				"price_list": "_Test Price List India",
-				"show_price": 1,
-			}
-		)
-
-		make_web_item_price(item_code="Test-Tshirt-Temp-S-R", price_list_rate=100)
-
-		frappe.local.shopping_cart_settings = None  # clear cached settings values
-		next_values = get_next_attribute_and_values(
-			"Test-Tshirt-Temp", selected_attributes={"Test Size": "Small", "Test Colour": "Red"}
-		)
-		print(">>>>", next_values)
-		price_info = next_values["product_info"]["price"]
-
-		self.assertEqual(next_values["exact_match"][0], "Test-Tshirt-Temp-S-R")
-		self.assertEqual(next_values["exact_match"][0], "Test-Tshirt-Temp-S-R")
-		self.assertEqual(price_info["price_list_rate"], 100.0)
-		self.assertEqual(price_info["formatted_price_sales_uom"], "₹ 100.00")
diff --git a/erpnext/e_commerce/variant_selector/utils.py b/erpnext/e_commerce/variant_selector/utils.py
deleted file mode 100644
index 88356f5..0000000
--- a/erpnext/e_commerce/variant_selector/utils.py
+++ /dev/null
@@ -1,251 +0,0 @@
-import frappe
-from frappe.utils import cint, flt
-
-from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import (
-	get_shopping_cart_settings,
-)
-from erpnext.e_commerce.shopping_cart.cart import _set_price_list
-from erpnext.e_commerce.variant_selector.item_variants_cache import ItemVariantsCacheManager
-from erpnext.utilities.product import get_price
-
-
-def get_item_codes_by_attributes(attribute_filters, template_item_code=None):
-	items = []
-
-	for attribute, values in attribute_filters.items():
-		attribute_values = values
-
-		if not isinstance(attribute_values, list):
-			attribute_values = [attribute_values]
-
-		if not attribute_values:
-			continue
-
-		wheres = []
-		query_values = []
-		for attribute_value in attribute_values:
-			wheres.append("( attribute = %s and attribute_value = %s )")
-			query_values += [attribute, attribute_value]
-
-		attribute_query = " or ".join(wheres)
-
-		if template_item_code:
-			variant_of_query = "AND t2.variant_of = %s"
-			query_values.append(template_item_code)
-		else:
-			variant_of_query = ""
-
-		query = """
-			SELECT
-				t1.parent
-			FROM
-				`tabItem Variant Attribute` t1
-			WHERE
-				1 = 1
-				AND (
-					{attribute_query}
-				)
-				AND EXISTS (
-					SELECT
-						1
-					FROM
-						`tabItem` t2
-					WHERE
-						t2.name = t1.parent
-						{variant_of_query}
-				)
-			GROUP BY
-				t1.parent
-			ORDER BY
-				NULL
-		""".format(
-			attribute_query=attribute_query, variant_of_query=variant_of_query
-		)
-
-		item_codes = set([r[0] for r in frappe.db.sql(query, query_values)])  # nosemgrep
-		items.append(item_codes)
-
-	res = list(set.intersection(*items))
-
-	return res
-
-
-@frappe.whitelist(allow_guest=True)
-def get_attributes_and_values(item_code):
-	"""Build a list of attributes and their possible values.
-	This will ignore the values upon selection of which there cannot exist one item.
-	"""
-	item_cache = ItemVariantsCacheManager(item_code)
-	item_variants_data = item_cache.get_item_variants_data()
-
-	attributes = get_item_attributes(item_code)
-	attribute_list = [a.attribute for a in attributes]
-
-	valid_options = {}
-	for item_code, attribute, attribute_value in item_variants_data:
-		if attribute in attribute_list:
-			valid_options.setdefault(attribute, set()).add(attribute_value)
-
-	item_attribute_values = frappe.db.get_all(
-		"Item Attribute Value", ["parent", "attribute_value", "idx"], order_by="parent asc, idx asc"
-	)
-	ordered_attribute_value_map = frappe._dict()
-	for iv in item_attribute_values:
-		ordered_attribute_value_map.setdefault(iv.parent, []).append(iv.attribute_value)
-
-	# build attribute values in idx order
-	for attr in attributes:
-		valid_attribute_values = valid_options.get(attr.attribute, [])
-		ordered_values = ordered_attribute_value_map.get(attr.attribute, [])
-		attr["values"] = [v for v in ordered_values if v in valid_attribute_values]
-
-	return attributes
-
-
-@frappe.whitelist(allow_guest=True)
-def get_next_attribute_and_values(item_code, selected_attributes):
-	from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
-
-	"""Find the count of Items that match the selected attributes.
-	Also, find the attribute values that are not applicable for further searching.
-	If less than equal to 10 items are found, return item_codes of those items.
-	If one item is matched exactly, return item_code of that item.
-	"""
-	selected_attributes = frappe.parse_json(selected_attributes)
-
-	item_cache = ItemVariantsCacheManager(item_code)
-	item_variants_data = item_cache.get_item_variants_data()
-
-	attributes = get_item_attributes(item_code)
-	attribute_list = [a.attribute for a in attributes]
-	filtered_items = get_items_with_selected_attributes(item_code, selected_attributes)
-
-	next_attribute = None
-
-	for attribute in attribute_list:
-		if attribute not in selected_attributes:
-			next_attribute = attribute
-			break
-
-	valid_options_for_attributes = frappe._dict()
-
-	for a in attribute_list:
-		valid_options_for_attributes[a] = set()
-
-		selected_attribute = selected_attributes.get(a, None)
-		if selected_attribute:
-			# already selected attribute values are valid options
-			valid_options_for_attributes[a].add(selected_attribute)
-
-	for row in item_variants_data:
-		item_code, attribute, attribute_value = row
-		if (
-			item_code in filtered_items
-			and attribute not in selected_attributes
-			and attribute in attribute_list
-		):
-			valid_options_for_attributes[attribute].add(attribute_value)
-
-	optional_attributes = item_cache.get_optional_attributes()
-	exact_match = []
-	# search for exact match if all selected attributes are required attributes
-	if len(selected_attributes.keys()) >= (len(attribute_list) - len(optional_attributes)):
-		item_attribute_value_map = item_cache.get_item_attribute_value_map()
-		for item_code, attr_dict in item_attribute_value_map.items():
-			if item_code in filtered_items and set(attr_dict.keys()) == set(selected_attributes.keys()):
-				exact_match.append(item_code)
-
-	filtered_items_count = len(filtered_items)
-
-	# get product info if exact match
-	# from erpnext.e_commerce.shopping_cart.product_info import get_product_info_for_website
-	if exact_match:
-		cart_settings = get_shopping_cart_settings()
-		product_info = get_item_variant_price_dict(exact_match[0], cart_settings)
-
-		if product_info:
-			product_info["is_stock_item"] = frappe.get_cached_value("Item", exact_match[0], "is_stock_item")
-			product_info["allow_items_not_in_stock"] = cint(cart_settings.allow_items_not_in_stock)
-	else:
-		product_info = None
-
-	product_id = ""
-	warehouse = ""
-	if exact_match or filtered_items:
-		if exact_match and len(exact_match) == 1:
-			product_id = exact_match[0]
-		elif filtered_items_count == 1:
-			product_id = list(filtered_items)[0]
-
-	if product_id:
-		warehouse = frappe.get_cached_value(
-			"Website Item", {"item_code": product_id}, "website_warehouse"
-		)
-
-	available_qty = 0.0
-	if warehouse and frappe.get_cached_value("Warehouse", warehouse, "is_group") == 1:
-		warehouses = get_child_warehouses(warehouse)
-	else:
-		warehouses = [warehouse] if warehouse else []
-
-	for warehouse in warehouses:
-		available_qty += flt(
-			frappe.db.get_value("Bin", {"item_code": product_id, "warehouse": warehouse}, "actual_qty")
-		)
-
-	return {
-		"next_attribute": next_attribute,
-		"valid_options_for_attributes": valid_options_for_attributes,
-		"filtered_items_count": filtered_items_count,
-		"filtered_items": filtered_items if filtered_items_count < 10 else [],
-		"exact_match": exact_match,
-		"product_info": product_info,
-		"available_qty": available_qty,
-	}
-
-
-def get_items_with_selected_attributes(item_code, selected_attributes):
-	item_cache = ItemVariantsCacheManager(item_code)
-	attribute_value_item_map = item_cache.get_attribute_value_item_map()
-
-	items = []
-	for attribute, value in selected_attributes.items():
-		filtered_items = attribute_value_item_map.get((attribute, value), [])
-		items.append(set(filtered_items))
-
-	return set.intersection(*items)
-
-
-# utilities
-
-
-def get_item_attributes(item_code):
-	attributes = frappe.db.get_all(
-		"Item Variant Attribute",
-		fields=["attribute"],
-		filters={"parenttype": "Item", "parent": item_code},
-		order_by="idx asc",
-	)
-
-	optional_attributes = ItemVariantsCacheManager(item_code).get_optional_attributes()
-
-	for a in attributes:
-		if a.attribute in optional_attributes:
-			a.optional = True
-
-	return attributes
-
-
-def get_item_variant_price_dict(item_code, cart_settings):
-	if cart_settings.enabled and cart_settings.show_price:
-		is_guest = frappe.session.user == "Guest"
-		# Show Price if logged in.
-		# If not logged in, check if price is hidden for guest.
-		if not is_guest or not cart_settings.hide_price_for_guest:
-			price_list = _set_price_list(cart_settings, None)
-			price = get_price(
-				item_code, price_list, cart_settings.default_customer_group, cart_settings.company
-			)
-			return {"price": price}
-
-	return None
diff --git a/erpnext/e_commerce/web_template/__init__.py b/erpnext/e_commerce/web_template/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/e_commerce/web_template/__init__.py
+++ /dev/null
diff --git a/erpnext/e_commerce/web_template/hero_slider/__init__.py b/erpnext/e_commerce/web_template/hero_slider/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/e_commerce/web_template/hero_slider/__init__.py
+++ /dev/null
diff --git a/erpnext/e_commerce/web_template/hero_slider/hero_slider.html b/erpnext/e_commerce/web_template/hero_slider/hero_slider.html
deleted file mode 100644
index fe4fee3..0000000
--- a/erpnext/e_commerce/web_template/hero_slider/hero_slider.html
+++ /dev/null
@@ -1,86 +0,0 @@
-{%- macro slide(image, title, subtitle, action, label, index, align="Left", theme="Dark") -%}
-{%- set align_class = resolve_class({
-	'text-right': align == 'Right',
-	'text-center': align == 'Centre',
-	'text-left': align == 'Left',
-}) -%}
-
-{%- set heading_class = resolve_class({
-	'text-white': theme == 'Dark',
-	'': theme == 'Light',
-}) -%}
-<div class="carousel-item {{ 'active' if index=='1' else ''}}" style="height: 450px;">
-	<img class="d-block h-100 w-100" style="object-fit: cover;" src="{{ image }}" alt="{{ title }}">
-	{%- if title or subtitle -%}
-	<div class="carousel-body container d-flex {{ align_class }}">
-		<div class="carousel-content align-self-center">
-			{%- if title -%}<h1 class="{{ heading_class }}">{{ title }}</h1>{%- endif -%}
-			{%- if subtitle -%}<p class="{{ heading_class }} mt-2">{{ subtitle }}</p>{%- endif -%}
-			{%- if action -%}
-			<a href="{{ action }}" class="btn btn-primary mt-3">
-				{{ label }}
-			</a>
-			{%- endif -%}
-		</div>
-	</div>
-	{%- endif -%}
-</div>
-{%- endmacro -%}
-
-{%- set hero_slider_id = 'id-' + frappe.utils.generate_hash('HeroSlider', 12) -%}
-
-<div id="{{ hero_slider_id }}" class="section-carousel carousel slide" data-ride="carousel">
-	{%- if show_indicators -%}
-	<ol class="carousel-indicators">
-		{%- for index in ['1', '2', '3', '4', '5'] -%}
-		{%- if values['slide_' + index + '_image'] -%}
-			<li data-target="#{{ hero_slider_id }}" data-slide-to="{{ frappe.utils.cint(index) - 1 }}" class="{{ 'active' if index=='1' else ''}}"></li>
-		{%- endif -%}
-		{%- endfor -%}
-	</ol>
-	{%- endif -%}
-	<div class="carousel-inner {{ resolve_class({'rounded-carousel': rounded }) }}">
-		{%- for index in ['1', '2', '3', '4', '5'] -%}
-			{%- set image = values['slide_' + index + '_image'] -%}
-			{%- set title = values['slide_' + index + '_title'] -%}
-			{%- set subtitle = values['slide_' + index + '_subtitle'] -%}
-			{%- set primary_action = values['slide_' + index + '_primary_action'] -%}
-			{%- set primary_action_label = values['slide_' + index + '_primary_action_label'] -%}
-			{%- set align = values['slide_' + index + '_content_align'] -%}
-			{%- set theme = values['slide_' + index + '_theme'] -%}
-
-			{%- if image -%}
-				{{ slide(image, title, subtitle, primary_action, primary_action_label, index, align, theme) }}
-			{%- endif -%}
-
-		{%- endfor -%}
-	</div>
-	{%- if show_controls -%}
-	<a class="carousel-control-prev" href="#{{ hero_slider_id }}" role="button" data-slide="prev">
-		<div class="carousel-control">
-			<svg class="mr-1" width="20" height="20" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
-				<path d="M11.625 3.75L6.375 9L11.625 14.25" stroke="#4C5A67" stroke-linecap="round" stroke-linejoin="round"/>
-			</svg>
-		</div>
-		<span class="sr-only">Previous</span>
-	</a>
-	<a class="carousel-control-next" href="#{{ hero_slider_id }}" role="button" data-slide="next">
-		<div class="carousel-control">
-			<svg class="ml-1" width="20" height="20" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
-				<path d="M6.375 14.25L11.625 9L6.375 3.75" stroke="#4C5A67" stroke-linecap="round" stroke-linejoin="round"/>
-			</svg>
-		</div>
-		<span class="sr-only">Next</span>
-	</a>
-	{%- endif -%}
-</div>
-
-<script>
-	frappe.ready(function () {
-		$('.carousel').carousel({
-			interval: false,
-			pause: "hover",
-			wrap: true
-		})
-	});
-</script>
diff --git a/erpnext/e_commerce/web_template/hero_slider/hero_slider.json b/erpnext/e_commerce/web_template/hero_slider/hero_slider.json
deleted file mode 100644
index 39b2b3e..0000000
--- a/erpnext/e_commerce/web_template/hero_slider/hero_slider.json
+++ /dev/null
@@ -1,288 +0,0 @@
-{
- "__unsaved": 1,
- "creation": "2020-11-17 15:21:51.207221",
- "docstatus": 0,
- "doctype": "Web Template",
- "fields": [
-  {
-   "fieldname": "slider_name",
-   "fieldtype": "Data",
-   "label": "Slider Name",
-   "reqd": 1
-  },
-  {
-   "default": "1",
-   "fieldname": "show_indicators",
-   "fieldtype": "Check",
-   "label": "Show Indicators",
-   "reqd": 0
-  },
-  {
-   "default": "1",
-   "fieldname": "show_controls",
-   "fieldtype": "Check",
-   "label": "Show Controls",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_1",
-   "fieldtype": "Section Break",
-   "label": "Slide 1",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_1_image",
-   "fieldtype": "Attach Image",
-   "label": "Image",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_1_title",
-   "fieldtype": "Data",
-   "label": "Title",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_1_subtitle",
-   "fieldtype": "Small Text",
-   "label": "Subtitle",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_1_primary_action_label",
-   "fieldtype": "Data",
-   "label": "Primary Action Label",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_1_primary_action",
-   "fieldtype": "Data",
-   "label": "Primary Action",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_1_content_align",
-   "fieldtype": "Select",
-   "label": "Content Align",
-   "options": "Left\nCentre\nRight",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_1_theme",
-   "fieldtype": "Select",
-   "label": "Slide Theme",
-   "options": "Dark\nLight",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_2",
-   "fieldtype": "Section Break",
-   "label": "Slide 2",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_2_image",
-   "fieldtype": "Attach Image",
-   "label": "Image ",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_2_title",
-   "fieldtype": "Data",
-   "label": "Title ",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_2_subtitle",
-   "fieldtype": "Small Text",
-   "label": "Subtitle ",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_2_primary_action_label",
-   "fieldtype": "Data",
-   "label": "Primary Action Label ",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_2_primary_action",
-   "fieldtype": "Data",
-   "label": "Primary Action ",
-   "reqd": 0
-  },
-  {
-   "default": "Left",
-   "fieldname": "slide_2_content_align",
-   "fieldtype": "Select",
-   "label": "Content Align",
-   "options": "Left\nCentre\nRight",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_2_theme",
-   "fieldtype": "Select",
-   "label": "Slide Theme",
-   "options": "Dark\nLight",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_3",
-   "fieldtype": "Section Break",
-   "label": "Slide 3",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_3_image",
-   "fieldtype": "Attach Image",
-   "label": "Image",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_3_title",
-   "fieldtype": "Data",
-   "label": "Title",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_3_subtitle",
-   "fieldtype": "Small Text",
-   "label": "Subtitle",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_3_primary_action_label",
-   "fieldtype": "Data",
-   "label": "Primary Action Label",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_3_primary_action",
-   "fieldtype": "Data",
-   "label": "Primary Action",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_3_content_align",
-   "fieldtype": "Select",
-   "label": "Content Align",
-   "options": "Left\nCentre\nRight",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_3_theme",
-   "fieldtype": "Select",
-   "label": "Slide Theme",
-   "options": "Dark\nLight",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_4",
-   "fieldtype": "Section Break",
-   "label": "Slide 4",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_4_image",
-   "fieldtype": "Attach Image",
-   "label": "Image",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_4_title",
-   "fieldtype": "Data",
-   "label": "Title",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_4_subtitle",
-   "fieldtype": "Small Text",
-   "label": "Subtitle",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_4_primary_action_label",
-   "fieldtype": "Data",
-   "label": "Primary Action Label",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_4_primary_action",
-   "fieldtype": "Data",
-   "label": "Primary Action",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_4_content_align",
-   "fieldtype": "Select",
-   "label": "Content Align",
-   "options": "Left\nCentre\nRight",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_4_theme",
-   "fieldtype": "Select",
-   "label": "Slide Theme",
-   "options": "Dark\nLight",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_5",
-   "fieldtype": "Section Break",
-   "label": "Slide 5",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_5_image",
-   "fieldtype": "Attach Image",
-   "label": "Image",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_5_title",
-   "fieldtype": "Data",
-   "label": "Title",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_5_subtitle",
-   "fieldtype": "Small Text",
-   "label": "Subtitle",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_5_primary_action_label",
-   "fieldtype": "Data",
-   "label": "Primary Action Label",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_5_primary_action",
-   "fieldtype": "Data",
-   "label": "Primary Action",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_5_content_align",
-   "fieldtype": "Select",
-   "label": "Content Align",
-   "options": "Left\nCentre\nRight",
-   "reqd": 0
-  },
-  {
-   "fieldname": "slide_5_theme",
-   "fieldtype": "Select",
-   "label": "Slide Theme",
-   "options": "Dark\nLight",
-   "reqd": 0
-  }
- ],
- "idx": 2,
- "modified": "2023-05-12 15:03:57.604060",
- "modified_by": "Administrator",
- "module": "E-commerce",
- "name": "Hero Slider",
- "owner": "Administrator",
- "standard": 1,
- "template": "",
- "type": "Section"
-}
\ No newline at end of file
diff --git a/erpnext/e_commerce/web_template/item_card_group/__init__.py b/erpnext/e_commerce/web_template/item_card_group/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/e_commerce/web_template/item_card_group/__init__.py
+++ /dev/null
diff --git a/erpnext/e_commerce/web_template/item_card_group/item_card_group.html b/erpnext/e_commerce/web_template/item_card_group/item_card_group.html
deleted file mode 100644
index 07952f0..0000000
--- a/erpnext/e_commerce/web_template/item_card_group/item_card_group.html
+++ /dev/null
@@ -1,37 +0,0 @@
-{% from "erpnext/templates/includes/macros.html" import item_card, item_card_body %}
-
-<div class="section-with-cards item-card-group-section">
-	<div class="item-group-header d-flex justify-content-between">
-		<div class="title-section">
-			{%- if title -%}
-			<h2 class="section-title">{{ title }}</h2>
-			{%- endif -%}
-			{%- if subtitle -%}
-			<p class="section-description">{{ subtitle }}</p>
-			{%- endif -%}
-		</div>
-		<div class="primary-action-section">
-			{%- if primary_action -%}
-			<a href="{{ action }}" class="btn btn-primary pull-right">
-				{{ primary_action_label }}
-			</a>
-			{%- endif -%}
-		</div>
-	</div>
-
-	<div class="row">
-		{%- for index in ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'] -%}
-		{%- set item = values['card_' + index + '_item'] -%}
-			{%- if item -%}
-				{%- set web_item = frappe.get_doc("Website Item", item) -%}
-				{{ item_card(
-					web_item, is_featured=values['card_' + index + '_featured'],
-					is_full_width=True, align="Center"
-				) }}
-			{%- endif -%}
-		{%- endfor -%}
-	</div>
-</div>
-
-<style>
-</style>
diff --git a/erpnext/e_commerce/web_template/item_card_group/item_card_group.json b/erpnext/e_commerce/web_template/item_card_group/item_card_group.json
deleted file mode 100644
index ad9e2a7..0000000
--- a/erpnext/e_commerce/web_template/item_card_group/item_card_group.json
+++ /dev/null
@@ -1,270 +0,0 @@
-{
- "__unsaved": 1,
- "creation": "2020-11-17 15:35:05.285322",
- "docstatus": 0,
- "doctype": "Web Template",
- "fields": [
-  {
-   "fieldname": "title",
-   "fieldtype": "Data",
-   "label": "Title",
-   "reqd": 1
-  },
-  {
-   "fieldname": "subtitle",
-   "fieldtype": "Data",
-   "label": "Subtitle",
-   "reqd": 0
-  },
-  {
-   "fieldname": "primary_action_label",
-   "fieldtype": "Data",
-   "label": "Primary Action Label",
-   "reqd": 0
-  },
-  {
-   "fieldname": "primary_action",
-   "fieldtype": "Data",
-   "label": "Primary Action",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_1",
-   "fieldtype": "Section Break",
-   "label": "Card 1",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_1_item",
-   "fieldtype": "Link",
-   "label": "Website Item",
-   "options": "Website Item",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_1_featured",
-   "fieldtype": "Check",
-   "label": "Featured",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_2",
-   "fieldtype": "Section Break",
-   "label": "Card 2",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_2_item",
-   "fieldtype": "Link",
-   "label": "Website Item",
-   "options": "Website Item",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_2_featured",
-   "fieldtype": "Check",
-   "label": "Featured",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_3",
-   "fieldtype": "Section Break",
-   "label": "Card 3",
-   "options": "",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_3_item",
-   "fieldtype": "Link",
-   "label": "Website Item",
-   "options": "Website Item",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_3_featured",
-   "fieldtype": "Check",
-   "label": "Featured",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_4",
-   "fieldtype": "Section Break",
-   "label": "Card 4",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_4_item",
-   "fieldtype": "Link",
-   "label": "Website Item",
-   "options": "Website Item",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_4_featured",
-   "fieldtype": "Check",
-   "label": "Featured",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_5",
-   "fieldtype": "Section Break",
-   "label": "Card 5",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_5_item",
-   "fieldtype": "Link",
-   "label": "Website Item",
-   "options": "Website Item",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_5_featured",
-   "fieldtype": "Check",
-   "label": "Featured",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_6",
-   "fieldtype": "Section Break",
-   "label": "Card 6",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_6_item",
-   "fieldtype": "Link",
-   "label": "Website Item",
-   "options": "Website Item",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_6_featured",
-   "fieldtype": "Check",
-   "label": "Featured",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_7",
-   "fieldtype": "Section Break",
-   "label": "Card 7",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_7_item",
-   "fieldtype": "Link",
-   "label": "Website Item",
-   "options": "Website Item",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_7_featured",
-   "fieldtype": "Check",
-   "label": "Featured",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_8",
-   "fieldtype": "Section Break",
-   "label": "Card 8",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_8_item",
-   "fieldtype": "Link",
-   "label": "Website Item",
-   "options": "Website Item",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_8_featured",
-   "fieldtype": "Check",
-   "label": "Featured",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_9",
-   "fieldtype": "Section Break",
-   "label": "Card 9",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_9_item",
-   "fieldtype": "Link",
-   "label": "Website Item",
-   "options": "Website Item",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_9_featured",
-   "fieldtype": "Check",
-   "label": "Featured",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_10",
-   "fieldtype": "Section Break",
-   "label": "Card 10",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_10_item",
-   "fieldtype": "Link",
-   "label": "Website Item",
-   "options": "Website Item",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_10_featured",
-   "fieldtype": "Check",
-   "label": "Featured",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_11",
-   "fieldtype": "Section Break",
-   "label": "Card 11",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_11_item",
-   "fieldtype": "Link",
-   "label": "Website Item",
-   "options": "Website Item",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_11_featured",
-   "fieldtype": "Check",
-   "label": "Featured",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_12",
-   "fieldtype": "Section Break",
-   "label": "Card 12",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_12_item",
-   "fieldtype": "Link",
-   "label": "Website Item",
-   "options": "Website Item",
-   "reqd": 0
-  },
-  {
-   "fieldname": "card_12_featured",
-   "fieldtype": "Check",
-   "label": "Featured",
-   "reqd": 0
-  }
- ],
- "idx": 0,
- "modified": "2021-12-21 14:44:59.821335",
- "modified_by": "Administrator",
- "module": "E-commerce",
- "name": "Item Card Group",
- "owner": "Administrator",
- "standard": 1,
- "template": "",
- "type": "Section"
-}
\ No newline at end of file
diff --git a/erpnext/e_commerce/web_template/product_card/__init__.py b/erpnext/e_commerce/web_template/product_card/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/e_commerce/web_template/product_card/__init__.py
+++ /dev/null
diff --git a/erpnext/e_commerce/web_template/product_card/product_card.html b/erpnext/e_commerce/web_template/product_card/product_card.html
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/e_commerce/web_template/product_card/product_card.html
+++ /dev/null
diff --git a/erpnext/e_commerce/web_template/product_card/product_card.json b/erpnext/e_commerce/web_template/product_card/product_card.json
deleted file mode 100644
index 2eb7374..0000000
--- a/erpnext/e_commerce/web_template/product_card/product_card.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "__unsaved": 1,
- "creation": "2020-11-17 15:28:47.809342",
- "docstatus": 0,
- "doctype": "Web Template",
- "fields": [
-  {
-   "fieldname": "item",
-   "fieldtype": "Link",
-   "label": "Item",
-   "options": "Item",
-   "reqd": 0
-  },
-  {
-   "fieldname": "featured",
-   "fieldtype": "Check",
-   "label": "Featured",
-   "options": "",
-   "reqd": 0
-  }
- ],
- "idx": 0,
- "modified": "2021-02-24 16:05:17.926610",
- "modified_by": "Administrator",
- "module": "E-commerce",
- "name": "Product Card",
- "owner": "Administrator",
- "standard": 1,
- "template": "",
- "type": "Component"
-}
\ No newline at end of file
diff --git a/erpnext/e_commerce/web_template/product_category_cards/__init__.py b/erpnext/e_commerce/web_template/product_category_cards/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/e_commerce/web_template/product_category_cards/__init__.py
+++ /dev/null
diff --git a/erpnext/e_commerce/web_template/product_category_cards/product_category_cards.html b/erpnext/e_commerce/web_template/product_category_cards/product_category_cards.html
deleted file mode 100644
index 6d75a8b..0000000
--- a/erpnext/e_commerce/web_template/product_category_cards/product_category_cards.html
+++ /dev/null
@@ -1,47 +0,0 @@
-{%- macro card(title, image, url, text_primary=False) -%}
-{%- set align_class = resolve_class({
-	'text-right': text_primary,
-	'text-centre': align == 'Center',
-	'text-left': align == 'Left',
-}) -%}
-<div class="card h-100">
-	{% if image %}
-	<img class="card-img-top" src="{{ image }}" alt="{{ title }}" style="max-height: 200px;">
-	{% else %}
-	<div class="placeholder-div" style="max-height: 200px;">
-		<span class="placeholder">
-			{{ frappe.utils.get_abbr(title or '') }}
-		</span>
-	</div>
-	{% endif %}
-
-	<div class="card-body text-center text-muted small">
-		{{ title or '' }}
-	</div>
-	<a href="{{ url or '#' }}" class="stretched-link"></a>
-</div>
-{%- endmacro -%}
-
-<div class="section-with-cards product-category-section">
-	{%- if title -%}
-	<h2 class="section-title">{{ title }}</h2>
-	{%- endif -%}
-	{%- if subtitle -%}
-	<p class="section-description">{{ subtitle }}</p>
-	{%- endif -%}
-	<!-- {%- set card_size = card_size or 'Small' -%} -->
-	<div class="{{ resolve_class({'mt-6': title}) }}">
-		<div class="card-grid">
-			{%- for index in ['1', '2', '3', '4', '5', '6', '7', '8'] -%}
-			{%- set category = values['category_' + index] -%}
-				{%- if category -%}
-					{%- set category = frappe.get_doc("Item Group", category) -%}
-					{{ card(category.name, category.image, category.route) }}
-				{%- endif -%}
-			{%- endfor -%}
-		</div>
-	</div>
-</div>
-
-<style>
-</style>
diff --git a/erpnext/e_commerce/web_template/product_category_cards/product_category_cards.json b/erpnext/e_commerce/web_template/product_category_cards/product_category_cards.json
deleted file mode 100644
index 0202165..0000000
--- a/erpnext/e_commerce/web_template/product_category_cards/product_category_cards.json
+++ /dev/null
@@ -1,85 +0,0 @@
-{
- "__unsaved": 1,
- "creation": "2020-11-17 15:25:50.855934",
- "docstatus": 0,
- "doctype": "Web Template",
- "fields": [
-  {
-   "fieldname": "title",
-   "fieldtype": "Data",
-   "label": "Title",
-   "reqd": 1
-  },
-  {
-   "fieldname": "subtitle",
-   "fieldtype": "Data",
-   "label": "Subtitle",
-   "reqd": 0
-  },
-  {
-   "fieldname": "category_1",
-   "fieldtype": "Link",
-   "label": "Item Group",
-   "options": "Item Group",
-   "reqd": 0
-  },
-  {
-   "fieldname": "category_2",
-   "fieldtype": "Link",
-   "label": "Item Group",
-   "options": "Item Group",
-   "reqd": 0
-  },
-  {
-   "fieldname": "category_3",
-   "fieldtype": "Link",
-   "label": "Item Group",
-   "options": "Item Group",
-   "reqd": 0
-  },
-  {
-   "fieldname": "category_4",
-   "fieldtype": "Link",
-   "label": "Item Group",
-   "options": "Item Group",
-   "reqd": 0
-  },
-  {
-   "fieldname": "category_5",
-   "fieldtype": "Link",
-   "label": "Item Group",
-   "options": "Item Group",
-   "reqd": 0
-  },
-  {
-   "fieldname": "category_6",
-   "fieldtype": "Link",
-   "label": "Item Group",
-   "options": "Item Group",
-   "reqd": 0
-  },
-  {
-   "fieldname": "category_7",
-   "fieldtype": "Link",
-   "label": "Item Group",
-   "options": "Item Group",
-   "reqd": 0
-  },
-  {
-   "fieldname": "category_8",
-   "fieldtype": "Link",
-   "label": "Item Group",
-   "options": "Item Group",
-   "reqd": 0
-  }
- ],
- "idx": 0,
- "modified": "2021-02-24 16:03:33.835635",
- "modified_by": "Administrator",
- "module": "E-commerce",
- "name": "Product Category Cards",
- "owner": "Administrator",
- "standard": 1,
- "template": "",
- "type": "Section"
-}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_mandate/__init__.py b/erpnext/erpnext_integrations/doctype/gocardless_mandate/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/erpnext_integrations/doctype/gocardless_mandate/__init__.py
+++ /dev/null
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_mandate/gocardless_mandate.js b/erpnext/erpnext_integrations/doctype/gocardless_mandate/gocardless_mandate.js
deleted file mode 100644
index 37f9f7b..0000000
--- a/erpnext/erpnext_integrations/doctype/gocardless_mandate/gocardless_mandate.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2018, Frappe Technologies and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('GoCardless Mandate', {
-});
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_mandate/gocardless_mandate.json b/erpnext/erpnext_integrations/doctype/gocardless_mandate/gocardless_mandate.json
deleted file mode 100644
index edf652c..0000000
--- a/erpnext/erpnext_integrations/doctype/gocardless_mandate/gocardless_mandate.json
+++ /dev/null
@@ -1,184 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "field:mandate",
- "beta": 0,
- "creation": "2018-02-08 11:33:15.721919",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
- "fields": [
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "disabled",
-   "fieldtype": "Check",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Disabled",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "customer",
-   "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Customer",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Customer",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "mandate",
-   "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Mandate",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "gocardless_customer",
-   "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "GoCardless Customer",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-02-11 12:28:03.183095",
- "modified_by": "Administrator",
- "module": "ERPNext Integrations",
- "name": "GoCardless Mandate",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [
-  {
-   "amend": 0,
-   "apply_user_permissions": 0,
-   "cancel": 0,
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "if_owner": 0,
-   "import": 0,
-   "permlevel": 0,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "System Manager",
-   "set_user_permissions": 0,
-   "share": 1,
-   "submit": 0,
-   "write": 1
-  }
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
-}
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_mandate/gocardless_mandate.py b/erpnext/erpnext_integrations/doctype/gocardless_mandate/gocardless_mandate.py
deleted file mode 100644
index bceb3ca..0000000
--- a/erpnext/erpnext_integrations/doctype/gocardless_mandate/gocardless_mandate.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class GoCardlessMandate(Document):
-	pass
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_mandate/test_gocardless_mandate.py b/erpnext/erpnext_integrations/doctype/gocardless_mandate/test_gocardless_mandate.py
deleted file mode 100644
index 0c1952a..0000000
--- a/erpnext/erpnext_integrations/doctype/gocardless_mandate/test_gocardless_mandate.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestGoCardlessMandate(unittest.TestCase):
-	pass
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_settings/__init__.py b/erpnext/erpnext_integrations/doctype/gocardless_settings/__init__.py
deleted file mode 100644
index 65be599..0000000
--- a/erpnext/erpnext_integrations/doctype/gocardless_settings/__init__.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-import hashlib
-import hmac
-import json
-
-import frappe
-
-
-@frappe.whitelist(allow_guest=True)
-def webhooks():
-	r = frappe.request
-	if not r:
-		return
-
-	if not authenticate_signature(r):
-		raise frappe.AuthenticationError
-
-	gocardless_events = json.loads(r.get_data()) or []
-	for event in gocardless_events["events"]:
-		set_status(event)
-
-	return 200
-
-
-def set_status(event):
-	resource_type = event.get("resource_type", {})
-
-	if resource_type == "mandates":
-		set_mandate_status(event)
-
-
-def set_mandate_status(event):
-	mandates = []
-	if isinstance(event["links"], (list,)):
-		for link in event["links"]:
-			mandates.append(link["mandate"])
-	else:
-		mandates.append(event["links"]["mandate"])
-
-	if (
-		event["action"] == "pending_customer_approval"
-		or event["action"] == "pending_submission"
-		or event["action"] == "submitted"
-		or event["action"] == "active"
-	):
-		disabled = 0
-	else:
-		disabled = 1
-
-	for mandate in mandates:
-		frappe.db.set_value("GoCardless Mandate", mandate, "disabled", disabled)
-
-
-def authenticate_signature(r):
-	"""Returns True if the received signature matches the generated signature"""
-	received_signature = frappe.get_request_header("Webhook-Signature")
-
-	if not received_signature:
-		return False
-
-	for key in get_webhook_keys():
-		computed_signature = hmac.new(key.encode("utf-8"), r.get_data(), hashlib.sha256).hexdigest()
-		if hmac.compare_digest(str(received_signature), computed_signature):
-			return True
-
-	return False
-
-
-def get_webhook_keys():
-	def _get_webhook_keys():
-		webhook_keys = [
-			d.webhooks_secret
-			for d in frappe.get_all(
-				"GoCardless Settings",
-				fields=["webhooks_secret"],
-			)
-			if d.webhooks_secret
-		]
-
-		return webhook_keys
-
-	return frappe.cache().get_value("gocardless_webhooks_secret", _get_webhook_keys)
-
-
-def clear_cache():
-	frappe.cache().delete_value("gocardless_webhooks_secret")
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.js b/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.js
deleted file mode 100644
index 2411297..0000000
--- a/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2018, Frappe Technologies and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('GoCardless Settings', {
-	refresh: function(frm) {
-		erpnext.utils.check_payments_app();
-	}
-});
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.json b/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.json
deleted file mode 100644
index cca3653..0000000
--- a/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.json
+++ /dev/null
@@ -1,211 +0,0 @@
-{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "field:gateway_name",
- "beta": 0,
- "creation": "2018-02-06 16:11:10.028249",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
- "fields": [
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "gateway_name",
-   "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Payment Gateway Name",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "section_break_2",
-   "fieldtype": "Section Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "access_token",
-   "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Access Token",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "webhooks_secret",
-   "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Webhooks Secret",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "use_sandbox",
-   "fieldtype": "Check",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Use Sandbox",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "unique": 0
-  }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2022-02-12 14:18:47.209114",
- "modified_by": "Administrator",
- "module": "ERPNext Integrations",
- "name": "GoCardless Settings",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [
-  {
-   "amend": 0,
-   "apply_user_permissions": 0,
-   "cancel": 0,
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "if_owner": 0,
-   "import": 0,
-   "permlevel": 0,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "System Manager",
-   "set_user_permissions": 0,
-   "share": 1,
-   "submit": 0,
-   "write": 1
-  }
- ],
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
-}
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.py b/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.py
deleted file mode 100644
index 4a29a6a..0000000
--- a/erpnext/erpnext_integrations/doctype/gocardless_settings/gocardless_settings.py
+++ /dev/null
@@ -1,220 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-from urllib.parse import urlencode
-
-import frappe
-import gocardless_pro
-from frappe import _
-from frappe.integrations.utils import create_request_log
-from frappe.model.document import Document
-from frappe.utils import call_hook_method, cint, flt, get_url
-
-from erpnext.utilities import payment_app_import_guard
-
-
-class GoCardlessSettings(Document):
-	supported_currencies = ["EUR", "DKK", "GBP", "SEK", "AUD", "NZD", "CAD", "USD"]
-
-	def validate(self):
-		self.initialize_client()
-
-	def initialize_client(self):
-		self.environment = self.get_environment()
-		try:
-			self.client = gocardless_pro.Client(
-				access_token=self.access_token, environment=self.environment
-			)
-			return self.client
-		except Exception as e:
-			frappe.throw(e)
-
-	def on_update(self):
-		with payment_app_import_guard():
-			from payments.utils import create_payment_gateway
-
-		create_payment_gateway(
-			"GoCardless-" + self.gateway_name, settings="GoCardLess Settings", controller=self.gateway_name
-		)
-		call_hook_method("payment_gateway_enabled", gateway="GoCardless-" + self.gateway_name)
-
-	def on_payment_request_submission(self, data):
-		if data.reference_doctype != "Fees":
-			customer_data = frappe.db.get_value(
-				data.reference_doctype, data.reference_name, ["company", "customer_name"], as_dict=1
-			)
-
-		data = {
-			"amount": flt(data.grand_total, data.precision("grand_total")),
-			"title": customer_data.company.encode("utf-8"),
-			"description": data.subject.encode("utf-8"),
-			"reference_doctype": data.doctype,
-			"reference_docname": data.name,
-			"payer_email": data.email_to or frappe.session.user,
-			"payer_name": customer_data.customer_name,
-			"order_id": data.name,
-			"currency": data.currency,
-		}
-
-		valid_mandate = self.check_mandate_validity(data)
-		if valid_mandate is not None:
-			data.update(valid_mandate)
-
-			self.create_payment_request(data)
-			return False
-		else:
-			return True
-
-	def check_mandate_validity(self, data):
-
-		if frappe.db.exists("GoCardless Mandate", dict(customer=data.get("payer_name"), disabled=0)):
-			registered_mandate = frappe.db.get_value(
-				"GoCardless Mandate", dict(customer=data.get("payer_name"), disabled=0), "mandate"
-			)
-			self.initialize_client()
-			mandate = self.client.mandates.get(registered_mandate)
-
-			if (
-				mandate.status == "pending_customer_approval"
-				or mandate.status == "pending_submission"
-				or mandate.status == "submitted"
-				or mandate.status == "active"
-			):
-				return {"mandate": registered_mandate}
-			else:
-				return None
-		else:
-			return None
-
-	def get_environment(self):
-		if self.use_sandbox:
-			return "sandbox"
-		else:
-			return "live"
-
-	def validate_transaction_currency(self, currency):
-		if currency not in self.supported_currencies:
-			frappe.throw(
-				_(
-					"Please select another payment method. Go Cardless does not support transactions in currency '{0}'"
-				).format(currency)
-			)
-
-	def get_payment_url(self, **kwargs):
-		return get_url("./integrations/gocardless_checkout?{0}".format(urlencode(kwargs)))
-
-	def create_payment_request(self, data):
-		self.data = frappe._dict(data)
-
-		try:
-			self.integration_request = create_request_log(self.data, "Host", "GoCardless")
-			return self.create_charge_on_gocardless()
-
-		except Exception:
-			frappe.log_error("Gocardless payment reqeust failed")
-			return {
-				"redirect_to": frappe.redirect_to_message(
-					_("Server Error"),
-					_(
-						"There seems to be an issue with the server's GoCardless configuration. Don't worry, in case of failure, the amount will get refunded to your account."
-					),
-				),
-				"status": 401,
-			}
-
-	def create_charge_on_gocardless(self):
-		redirect_to = self.data.get("redirect_to") or None
-		redirect_message = self.data.get("redirect_message") or None
-
-		reference_doc = frappe.get_doc(
-			self.data.get("reference_doctype"), self.data.get("reference_docname")
-		)
-		self.initialize_client()
-
-		try:
-			payment = self.client.payments.create(
-				params={
-					"amount": cint(reference_doc.grand_total * 100),
-					"currency": reference_doc.currency,
-					"links": {"mandate": self.data.get("mandate")},
-					"metadata": {
-						"reference_doctype": reference_doc.doctype,
-						"reference_document": reference_doc.name,
-					},
-				},
-				headers={
-					"Idempotency-Key": self.data.get("reference_docname"),
-				},
-			)
-
-			if (
-				payment.status == "pending_submission"
-				or payment.status == "pending_customer_approval"
-				or payment.status == "submitted"
-			):
-				self.integration_request.db_set("status", "Authorized", update_modified=False)
-				self.flags.status_changed_to = "Completed"
-				self.integration_request.db_set("output", payment.status, update_modified=False)
-
-			elif payment.status == "confirmed" or payment.status == "paid_out":
-				self.integration_request.db_set("status", "Completed", update_modified=False)
-				self.flags.status_changed_to = "Completed"
-				self.integration_request.db_set("output", payment.status, update_modified=False)
-
-			elif (
-				payment.status == "cancelled"
-				or payment.status == "customer_approval_denied"
-				or payment.status == "charged_back"
-			):
-				self.integration_request.db_set("status", "Cancelled", update_modified=False)
-				frappe.log_error("Gocardless payment cancelled")
-				self.integration_request.db_set("error", payment.status, update_modified=False)
-			else:
-				self.integration_request.db_set("status", "Failed", update_modified=False)
-				frappe.log_error("Gocardless payment failed")
-				self.integration_request.db_set("error", payment.status, update_modified=False)
-
-		except Exception as e:
-			frappe.log_error("GoCardless Payment Error")
-
-		if self.flags.status_changed_to == "Completed":
-			status = "Completed"
-			if "reference_doctype" in self.data and "reference_docname" in self.data:
-				custom_redirect_to = None
-				try:
-					custom_redirect_to = frappe.get_doc(
-						self.data.get("reference_doctype"), self.data.get("reference_docname")
-					).run_method("on_payment_authorized", self.flags.status_changed_to)
-				except Exception:
-					frappe.log_error("Gocardless redirect failed")
-
-				if custom_redirect_to:
-					redirect_to = custom_redirect_to
-
-			redirect_url = redirect_to
-		else:
-			status = "Error"
-			redirect_url = "payment-failed"
-
-			if redirect_message:
-				redirect_url += "&" + urlencode({"redirect_message": redirect_message})
-
-			redirect_url = get_url(redirect_url)
-
-		return {"redirect_to": redirect_url, "status": status}
-
-
-def get_gateway_controller(doc):
-	payment_request = frappe.get_doc("Payment Request", doc)
-	gateway_controller = frappe.db.get_value(
-		"Payment Gateway", payment_request.payment_gateway, "gateway_controller"
-	)
-	return gateway_controller
-
-
-def gocardless_initialization(doc):
-	gateway_controller = get_gateway_controller(doc)
-	settings = frappe.get_doc("GoCardless Settings", gateway_controller)
-	client = settings.initialize_client()
-	return client
diff --git a/erpnext/erpnext_integrations/doctype/gocardless_settings/test_gocardless_settings.py b/erpnext/erpnext_integrations/doctype/gocardless_settings/test_gocardless_settings.py
deleted file mode 100644
index 379afe5..0000000
--- a/erpnext/erpnext_integrations/doctype/gocardless_settings/test_gocardless_settings.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies and Contributors
-# See license.txt
-
-import unittest
-
-
-class TestGoCardlessSettings(unittest.TestCase):
-	pass
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/__init__.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/__init__.py
+++ /dev/null
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/account_balance.html b/erpnext/erpnext_integrations/doctype/mpesa_settings/account_balance.html
deleted file mode 100644
index b74a718..0000000
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/account_balance.html
+++ /dev/null
@@ -1,28 +0,0 @@
-
-{% if not jQuery.isEmptyObject(data) %}
-<h5 style="margin-top: 20px;"> {{ __("Balance Details") }} </h5>
-<table class="table table-bordered small">
-	<thead>
-		<tr>
-			<th style="width: 20%">{{ __("Account Type") }}</th>
-			<th style="width: 20%" class="text-right">{{ __("Current Balance") }}</th>
-			<th style="width: 20%" class="text-right">{{ __("Available Balance") }}</th>
-			<th style="width: 20%" class="text-right">{{ __("Reserved Balance") }}</th>
-			<th style="width: 20%" class="text-right">{{ __("Uncleared Balance") }}</th>
-		</tr>
-	</thead>
-	<tbody>
-		{% for(const [key, value] of Object.entries(data)) { %}
-			<tr>
-				<td> {%= key %} </td>
-				<td class="text-right"> {%= value["current_balance"] %} </td>
-				<td class="text-right"> {%= value["available_balance"] %} </td>
-				<td class="text-right"> {%= value["reserved_balance"] %} </td>
-				<td class="text-right"> {%= value["uncleared_balance"] %} </td>
-			</tr>
-		{% } %}
-	</tbody>
-</table>
-{% else %}
-<p style="margin-top: 30px;"> Account Balance Information Not Available. </p>
-{% endif %}
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
deleted file mode 100644
index a577e7f..0000000
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
+++ /dev/null
@@ -1,149 +0,0 @@
-import base64
-import datetime
-
-import requests
-from requests.auth import HTTPBasicAuth
-
-
-class MpesaConnector:
-	def __init__(
-		self,
-		env="sandbox",
-		app_key=None,
-		app_secret=None,
-		sandbox_url="https://sandbox.safaricom.co.ke",
-		live_url="https://api.safaricom.co.ke",
-	):
-		"""Setup configuration for Mpesa connector and generate new access token."""
-		self.env = env
-		self.app_key = app_key
-		self.app_secret = app_secret
-		if env == "sandbox":
-			self.base_url = sandbox_url
-		else:
-			self.base_url = live_url
-		self.authenticate()
-
-	def authenticate(self):
-		"""
-		This method is used to fetch the access token required by Mpesa.
-
-		Returns:
-		        access_token (str): This token is to be used with the Bearer header for further API calls to Mpesa.
-		"""
-		authenticate_uri = "/oauth/v1/generate?grant_type=client_credentials"
-		authenticate_url = "{0}{1}".format(self.base_url, authenticate_uri)
-		r = requests.get(authenticate_url, auth=HTTPBasicAuth(self.app_key, self.app_secret))
-		self.authentication_token = r.json()["access_token"]
-		return r.json()["access_token"]
-
-	def get_balance(
-		self,
-		initiator=None,
-		security_credential=None,
-		party_a=None,
-		identifier_type=None,
-		remarks=None,
-		queue_timeout_url=None,
-		result_url=None,
-	):
-		"""
-		This method uses Mpesa's Account Balance API to to enquire the balance on a M-Pesa BuyGoods (Till Number).
-
-		Args:
-		        initiator (str): Username used to authenticate the transaction.
-		        security_credential (str): Generate from developer portal.
-		        command_id (str): AccountBalance.
-		        party_a (int): Till number being queried.
-		        identifier_type (int): Type of organization receiving the transaction. (MSISDN/Till Number/Organization short code)
-		        remarks (str): Comments that are sent along with the transaction(maximum 100 characters).
-		        queue_timeout_url (str): The url that handles information of timed out transactions.
-		        result_url (str): The url that receives results from M-Pesa api call.
-
-		Returns:
-		        OriginatorConverstionID (str): The unique request ID for tracking a transaction.
-		        ConversationID (str): The unique request ID returned by mpesa for each request made
-		        ResponseDescription (str): Response Description message
-		"""
-
-		payload = {
-			"Initiator": initiator,
-			"SecurityCredential": security_credential,
-			"CommandID": "AccountBalance",
-			"PartyA": party_a,
-			"IdentifierType": identifier_type,
-			"Remarks": remarks,
-			"QueueTimeOutURL": queue_timeout_url,
-			"ResultURL": result_url,
-		}
-		headers = {
-			"Authorization": "Bearer {0}".format(self.authentication_token),
-			"Content-Type": "application/json",
-		}
-		saf_url = "{0}{1}".format(self.base_url, "/mpesa/accountbalance/v1/query")
-		r = requests.post(saf_url, headers=headers, json=payload)
-		return r.json()
-
-	def stk_push(
-		self,
-		business_shortcode=None,
-		passcode=None,
-		amount=None,
-		callback_url=None,
-		reference_code=None,
-		phone_number=None,
-		description=None,
-	):
-		"""
-		This method uses Mpesa's Express API to initiate online payment on behalf of a customer.
-
-		Args:
-		        business_shortcode (int): The short code of the organization.
-		        passcode (str): Get from developer portal
-		        amount (int): The amount being transacted
-		        callback_url (str): A CallBack URL is a valid secure URL that is used to receive notifications from M-Pesa API.
-		        reference_code(str): Account Reference: This is an Alpha-Numeric parameter that is defined by your system as an Identifier of the transaction for CustomerPayBillOnline transaction type.
-		        phone_number(int): The Mobile Number to receive the STK Pin Prompt.
-		        description(str): This is any additional information/comment that can be sent along with the request from your system. MAX 13 characters
-
-		Success Response:
-		        CustomerMessage(str): Messages that customers can understand.
-		        CheckoutRequestID(str): This is a global unique identifier of the processed checkout transaction request.
-		        ResponseDescription(str): Describes Success or failure
-		        MerchantRequestID(str): This is a global unique Identifier for any submitted payment request.
-		        ResponseCode(int): 0 means success all others are error codes. e.g.404.001.03
-
-		Error Reponse:
-		        requestId(str): This is a unique requestID for the payment request
-		        errorCode(str): This is a predefined code that indicates the reason for request failure.
-		        errorMessage(str): This is a predefined code that indicates the reason for request failure.
-		"""
-
-		time = (
-			str(datetime.datetime.now()).split(".")[0].replace("-", "").replace(" ", "").replace(":", "")
-		)
-		password = "{0}{1}{2}".format(str(business_shortcode), str(passcode), time)
-		encoded = base64.b64encode(bytes(password, encoding="utf8"))
-		payload = {
-			"BusinessShortCode": business_shortcode,
-			"Password": encoded.decode("utf-8"),
-			"Timestamp": time,
-			"Amount": amount,
-			"PartyA": int(phone_number),
-			"PartyB": reference_code,
-			"PhoneNumber": int(phone_number),
-			"CallBackURL": callback_url,
-			"AccountReference": reference_code,
-			"TransactionDesc": description,
-			"TransactionType": "CustomerPayBillOnline"
-			if self.env == "sandbox"
-			else "CustomerBuyGoodsOnline",
-		}
-		headers = {
-			"Authorization": "Bearer {0}".format(self.authentication_token),
-			"Content-Type": "application/json",
-		}
-
-		saf_url = "{0}{1}".format(self.base_url, "/mpesa/stkpush/v1/processrequest")
-		r = requests.post(saf_url, headers=headers, json=payload)
-		return r.json()
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py
deleted file mode 100644
index c92edc5..0000000
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py
+++ /dev/null
@@ -1,56 +0,0 @@
-import frappe
-from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
-
-
-def create_custom_pos_fields():
-	"""Create custom fields corresponding to POS Settings and POS Invoice."""
-	pos_field = {
-		"POS Invoice": [
-			{
-				"fieldname": "request_for_payment",
-				"label": "Request for Payment",
-				"fieldtype": "Button",
-				"hidden": 1,
-				"insert_after": "contact_email",
-			},
-			{
-				"fieldname": "mpesa_receipt_number",
-				"label": "Mpesa Receipt Number",
-				"fieldtype": "Data",
-				"read_only": 1,
-				"insert_after": "company",
-			},
-		]
-	}
-	if not frappe.get_meta("POS Invoice").has_field("request_for_payment"):
-		create_custom_fields(pos_field)
-
-	record_dict = [
-		{
-			"doctype": "POS Field",
-			"fieldname": "contact_mobile",
-			"label": "Mobile No",
-			"fieldtype": "Data",
-			"options": "Phone",
-			"parenttype": "POS Settings",
-			"parent": "POS Settings",
-			"parentfield": "invoice_fields",
-		},
-		{
-			"doctype": "POS Field",
-			"fieldname": "request_for_payment",
-			"label": "Request for Payment",
-			"fieldtype": "Button",
-			"parenttype": "POS Settings",
-			"parent": "POS Settings",
-			"parentfield": "invoice_fields",
-		},
-	]
-	create_pos_settings(record_dict)
-
-
-def create_pos_settings(record_dict):
-	for record in record_dict:
-		if frappe.db.exists("POS Field", {"fieldname": record.get("fieldname")}):
-			continue
-		frappe.get_doc(record).insert()
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.js b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.js
deleted file mode 100644
index 447d720..0000000
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.js
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Mpesa Settings', {
-	onload_post_render: function(frm) {
-		frm.events.setup_account_balance_html(frm);
-	},
-
-	refresh: function(frm) {
-		erpnext.utils.check_payments_app();
-
-		frappe.realtime.on("refresh_mpesa_dashboard", function(){
-			frm.reload_doc();
-			frm.events.setup_account_balance_html(frm);
-		});
-	},
-
-	get_account_balance: function(frm) {
-		if (!frm.doc.initiator_name && !frm.doc.security_credential) {
-			frappe.throw(__("Please set the initiator name and the security credential"));
-		}
-		frappe.call({
-			method: "get_account_balance_info",
-			doc: frm.doc
-		});
-	},
-
-	setup_account_balance_html: function(frm) {
-		if (!frm.doc.account_balance) return;
-		$("div").remove(".form-dashboard-section.custom");
-		frm.dashboard.add_section(
-			frappe.render_template('account_balance', {
-				data: JSON.parse(frm.doc.account_balance)
-			})
-		);
-		frm.dashboard.show();
-	}
-
-});
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json
deleted file mode 100644
index 8f3b427..0000000
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json
+++ /dev/null
@@ -1,152 +0,0 @@
-{
- "actions": [],
- "autoname": "field:payment_gateway_name",
- "creation": "2020-09-10 13:21:27.398088",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "payment_gateway_name",
-  "consumer_key",
-  "consumer_secret",
-  "initiator_name",
-  "till_number",
-  "transaction_limit",
-  "sandbox",
-  "column_break_4",
-  "business_shortcode",
-  "online_passkey",
-  "security_credential",
-  "get_account_balance",
-  "account_balance"
- ],
- "fields": [
-  {
-   "fieldname": "payment_gateway_name",
-   "fieldtype": "Data",
-   "label": "Payment Gateway Name",
-   "reqd": 1,
-   "unique": 1
-  },
-  {
-   "fieldname": "consumer_key",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Consumer Key",
-   "reqd": 1
-  },
-  {
-   "fieldname": "consumer_secret",
-   "fieldtype": "Password",
-   "in_list_view": 1,
-   "label": "Consumer Secret",
-   "reqd": 1
-  },
-  {
-   "fieldname": "till_number",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Till Number",
-   "reqd": 1
-  },
-  {
-   "default": "0",
-   "fieldname": "sandbox",
-   "fieldtype": "Check",
-   "label": "Sandbox"
-  },
-  {
-   "fieldname": "column_break_4",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "online_passkey",
-   "fieldtype": "Password",
-   "label": " Online PassKey",
-   "reqd": 1
-  },
-  {
-   "fieldname": "initiator_name",
-   "fieldtype": "Data",
-   "label": "Initiator Name"
-  },
-  {
-   "fieldname": "security_credential",
-   "fieldtype": "Small Text",
-   "label": "Security Credential"
-  },
-  {
-   "fieldname": "account_balance",
-   "fieldtype": "Long Text",
-   "hidden": 1,
-   "label": "Account Balance",
-   "read_only": 1
-  },
-  {
-   "fieldname": "get_account_balance",
-   "fieldtype": "Button",
-   "label": "Get Account Balance"
-  },
-  {
-   "depends_on": "eval:(doc.sandbox==0)",
-   "fieldname": "business_shortcode",
-   "fieldtype": "Data",
-   "label": "Business Shortcode",
-   "mandatory_depends_on": "eval:(doc.sandbox==0)"
-  },
-  {
-   "default": "150000",
-   "fieldname": "transaction_limit",
-   "fieldtype": "Float",
-   "label": "Transaction Limit",
-   "non_negative": 1
-  }
- ],
- "links": [],
- "modified": "2021-03-02 17:35:14.084342",
- "modified_by": "Administrator",
- "module": "ERPNext Integrations",
- "name": "Mpesa 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,
-   "delete": 1,
-   "email": 1,
-   "export": 1,
-   "print": 1,
-   "read": 1,
-   "report": 1,
-   "role": "Accounts User",
-   "share": 1,
-   "write": 1
-  }
- ],
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
deleted file mode 100644
index a298e11..0000000
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
+++ /dev/null
@@ -1,354 +0,0 @@
-# Copyright (c) 2020, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-from json import dumps, loads
-
-import frappe
-from frappe import _
-from frappe.integrations.utils import create_request_log
-from frappe.model.document import Document
-from frappe.utils import call_hook_method, fmt_money, get_request_site_address
-
-from erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_connector import MpesaConnector
-from erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_custom_fields import (
-	create_custom_pos_fields,
-)
-from erpnext.erpnext_integrations.utils import create_mode_of_payment
-from erpnext.utilities import payment_app_import_guard
-
-
-class MpesaSettings(Document):
-	supported_currencies = ["KES"]
-
-	def validate_transaction_currency(self, currency):
-		if currency not in self.supported_currencies:
-			frappe.throw(
-				_(
-					"Please select another payment method. Mpesa does not support transactions in currency '{0}'"
-				).format(currency)
-			)
-
-	def on_update(self):
-		with payment_app_import_guard():
-			from payments.utils import create_payment_gateway
-
-		create_custom_pos_fields()
-		create_payment_gateway(
-			"Mpesa-" + self.payment_gateway_name,
-			settings="Mpesa Settings",
-			controller=self.payment_gateway_name,
-		)
-		call_hook_method(
-			"payment_gateway_enabled", gateway="Mpesa-" + self.payment_gateway_name, payment_channel="Phone"
-		)
-
-		# required to fetch the bank account details from the payment gateway account
-		frappe.db.commit()
-		create_mode_of_payment("Mpesa-" + self.payment_gateway_name, payment_type="Phone")
-
-	def request_for_payment(self, **kwargs):
-		args = frappe._dict(kwargs)
-		request_amounts = self.split_request_amount_according_to_transaction_limit(args)
-
-		for i, amount in enumerate(request_amounts):
-			args.request_amount = amount
-			if frappe.flags.in_test:
-				from erpnext.erpnext_integrations.doctype.mpesa_settings.test_mpesa_settings import (
-					get_payment_request_response_payload,
-				)
-
-				response = frappe._dict(get_payment_request_response_payload(amount))
-			else:
-				response = frappe._dict(generate_stk_push(**args))
-
-			self.handle_api_response("CheckoutRequestID", args, response)
-
-	def split_request_amount_according_to_transaction_limit(self, args):
-		request_amount = args.request_amount
-		if request_amount > self.transaction_limit:
-			# make multiple requests
-			request_amounts = []
-			requests_to_be_made = frappe.utils.ceil(
-				request_amount / self.transaction_limit
-			)  # 480/150 = ceil(3.2) = 4
-			for i in range(requests_to_be_made):
-				amount = self.transaction_limit
-				if i == requests_to_be_made - 1:
-					amount = request_amount - (
-						self.transaction_limit * i
-					)  # for 4th request, 480 - (150 * 3) = 30
-				request_amounts.append(amount)
-		else:
-			request_amounts = [request_amount]
-
-		return request_amounts
-
-	@frappe.whitelist()
-	def get_account_balance_info(self):
-		payload = dict(
-			reference_doctype="Mpesa Settings", reference_docname=self.name, doc_details=vars(self)
-		)
-
-		if frappe.flags.in_test:
-			from erpnext.erpnext_integrations.doctype.mpesa_settings.test_mpesa_settings import (
-				get_test_account_balance_response,
-			)
-
-			response = frappe._dict(get_test_account_balance_response())
-		else:
-			response = frappe._dict(get_account_balance(payload))
-
-		self.handle_api_response("ConversationID", payload, response)
-
-	def handle_api_response(self, global_id, request_dict, response):
-		"""Response received from API calls returns a global identifier for each transaction, this code is returned during the callback."""
-		# check error response
-		if getattr(response, "requestId"):
-			req_name = getattr(response, "requestId")
-			error = response
-		else:
-			# global checkout id used as request name
-			req_name = getattr(response, global_id)
-			error = None
-
-		if not frappe.db.exists("Integration Request", req_name):
-			create_request_log(request_dict, "Host", "Mpesa", req_name, error)
-
-		if error:
-			frappe.throw(_(getattr(response, "errorMessage")), title=_("Transaction Error"))
-
-
-def generate_stk_push(**kwargs):
-	"""Generate stk push by making a API call to the stk push API."""
-	args = frappe._dict(kwargs)
-	try:
-		callback_url = (
-			get_request_site_address(True)
-			+ "/api/method/erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings.verify_transaction"
-		)
-
-		mpesa_settings = frappe.get_doc("Mpesa Settings", args.payment_gateway[6:])
-		env = "production" if not mpesa_settings.sandbox else "sandbox"
-		# for sandbox, business shortcode is same as till number
-		business_shortcode = (
-			mpesa_settings.business_shortcode if env == "production" else mpesa_settings.till_number
-		)
-
-		connector = MpesaConnector(
-			env=env,
-			app_key=mpesa_settings.consumer_key,
-			app_secret=mpesa_settings.get_password("consumer_secret"),
-		)
-
-		mobile_number = sanitize_mobile_number(args.sender)
-
-		response = connector.stk_push(
-			business_shortcode=business_shortcode,
-			amount=args.request_amount,
-			passcode=mpesa_settings.get_password("online_passkey"),
-			callback_url=callback_url,
-			reference_code=mpesa_settings.till_number,
-			phone_number=mobile_number,
-			description="POS Payment",
-		)
-
-		return response
-
-	except Exception:
-		frappe.log_error("Mpesa Express Transaction Error")
-		frappe.throw(
-			_("Issue detected with Mpesa configuration, check the error logs for more details"),
-			title=_("Mpesa Express Error"),
-		)
-
-
-def sanitize_mobile_number(number):
-	"""Add country code and strip leading zeroes from the phone number."""
-	return "254" + str(number).lstrip("0")
-
-
-@frappe.whitelist(allow_guest=True)
-def verify_transaction(**kwargs):
-	"""Verify the transaction result received via callback from stk."""
-	transaction_response = frappe._dict(kwargs["Body"]["stkCallback"])
-
-	checkout_id = getattr(transaction_response, "CheckoutRequestID", "")
-	if not isinstance(checkout_id, str):
-		frappe.throw(_("Invalid Checkout Request ID"))
-
-	integration_request = frappe.get_doc("Integration Request", checkout_id)
-	transaction_data = frappe._dict(loads(integration_request.data))
-	total_paid = 0  # for multiple integration request made against a pos invoice
-	success = False  # for reporting successfull callback to point of sale ui
-
-	if transaction_response["ResultCode"] == 0:
-		if integration_request.reference_doctype and integration_request.reference_docname:
-			try:
-				item_response = transaction_response["CallbackMetadata"]["Item"]
-				amount = fetch_param_value(item_response, "Amount", "Name")
-				mpesa_receipt = fetch_param_value(item_response, "MpesaReceiptNumber", "Name")
-				pr = frappe.get_doc(
-					integration_request.reference_doctype, integration_request.reference_docname
-				)
-
-				mpesa_receipts, completed_payments = get_completed_integration_requests_info(
-					integration_request.reference_doctype, integration_request.reference_docname, checkout_id
-				)
-
-				total_paid = amount + sum(completed_payments)
-				mpesa_receipts = ", ".join(mpesa_receipts + [mpesa_receipt])
-
-				if total_paid >= pr.grand_total:
-					pr.run_method("on_payment_authorized", "Completed")
-					success = True
-
-				frappe.db.set_value("POS Invoice", pr.reference_name, "mpesa_receipt_number", mpesa_receipts)
-				integration_request.handle_success(transaction_response)
-			except Exception:
-				integration_request.handle_failure(transaction_response)
-				frappe.log_error("Mpesa: Failed to verify transaction")
-
-	else:
-		integration_request.handle_failure(transaction_response)
-
-	frappe.publish_realtime(
-		event="process_phone_payment",
-		doctype="POS Invoice",
-		docname=transaction_data.payment_reference,
-		user=integration_request.owner,
-		message={
-			"amount": total_paid,
-			"success": success,
-			"failure_message": transaction_response["ResultDesc"]
-			if transaction_response["ResultCode"] != 0
-			else "",
-		},
-	)
-
-
-def get_completed_integration_requests_info(reference_doctype, reference_docname, checkout_id):
-	output_of_other_completed_requests = frappe.get_all(
-		"Integration Request",
-		filters={
-			"name": ["!=", checkout_id],
-			"reference_doctype": reference_doctype,
-			"reference_docname": reference_docname,
-			"status": "Completed",
-		},
-		pluck="output",
-	)
-
-	mpesa_receipts, completed_payments = [], []
-
-	for out in output_of_other_completed_requests:
-		out = frappe._dict(loads(out))
-		item_response = out["CallbackMetadata"]["Item"]
-		completed_amount = fetch_param_value(item_response, "Amount", "Name")
-		completed_mpesa_receipt = fetch_param_value(item_response, "MpesaReceiptNumber", "Name")
-		completed_payments.append(completed_amount)
-		mpesa_receipts.append(completed_mpesa_receipt)
-
-	return mpesa_receipts, completed_payments
-
-
-def get_account_balance(request_payload):
-	"""Call account balance API to send the request to the Mpesa Servers."""
-	try:
-		mpesa_settings = frappe.get_doc("Mpesa Settings", request_payload.get("reference_docname"))
-		env = "production" if not mpesa_settings.sandbox else "sandbox"
-		connector = MpesaConnector(
-			env=env,
-			app_key=mpesa_settings.consumer_key,
-			app_secret=mpesa_settings.get_password("consumer_secret"),
-		)
-
-		callback_url = (
-			get_request_site_address(True)
-			+ "/api/method/erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings.process_balance_info"
-		)
-
-		response = connector.get_balance(
-			mpesa_settings.initiator_name,
-			mpesa_settings.security_credential,
-			mpesa_settings.till_number,
-			4,
-			mpesa_settings.name,
-			callback_url,
-			callback_url,
-		)
-		return response
-	except Exception:
-		frappe.log_error("Mpesa: Failed to get account balance")
-		frappe.throw(_("Please check your configuration and try again"), title=_("Error"))
-
-
-@frappe.whitelist(allow_guest=True)
-def process_balance_info(**kwargs):
-	"""Process and store account balance information received via callback from the account balance API call."""
-	account_balance_response = frappe._dict(kwargs["Result"])
-
-	conversation_id = getattr(account_balance_response, "ConversationID", "")
-	if not isinstance(conversation_id, str):
-		frappe.throw(_("Invalid Conversation ID"))
-
-	request = frappe.get_doc("Integration Request", conversation_id)
-
-	if request.status == "Completed":
-		return
-
-	transaction_data = frappe._dict(loads(request.data))
-
-	if account_balance_response["ResultCode"] == 0:
-		try:
-			result_params = account_balance_response["ResultParameters"]["ResultParameter"]
-
-			balance_info = fetch_param_value(result_params, "AccountBalance", "Key")
-			balance_info = format_string_to_json(balance_info)
-
-			ref_doc = frappe.get_doc(transaction_data.reference_doctype, transaction_data.reference_docname)
-			ref_doc.db_set("account_balance", balance_info)
-
-			request.handle_success(account_balance_response)
-			frappe.publish_realtime(
-				"refresh_mpesa_dashboard",
-				doctype="Mpesa Settings",
-				docname=transaction_data.reference_docname,
-				user=transaction_data.owner,
-			)
-		except Exception:
-			request.handle_failure(account_balance_response)
-			frappe.log_error(
-				title="Mpesa Account Balance Processing Error", message=account_balance_response
-			)
-	else:
-		request.handle_failure(account_balance_response)
-
-
-def format_string_to_json(balance_info):
-	"""
-	Format string to json.
-
-	e.g: '''Working Account|KES|481000.00|481000.00|0.00|0.00'''
-	=> {'Working Account': {'current_balance': '481000.00',
-	        'available_balance': '481000.00',
-	        'reserved_balance': '0.00',
-	        'uncleared_balance': '0.00'}}
-	"""
-	balance_dict = frappe._dict()
-	for account_info in balance_info.split("&"):
-		account_info = account_info.split("|")
-		balance_dict[account_info[0]] = dict(
-			current_balance=fmt_money(account_info[2], currency="KES"),
-			available_balance=fmt_money(account_info[3], currency="KES"),
-			reserved_balance=fmt_money(account_info[4], currency="KES"),
-			uncleared_balance=fmt_money(account_info[5], currency="KES"),
-		)
-	return dumps(balance_dict)
-
-
-def fetch_param_value(response, key, key_field):
-	"""Fetch the specified key from list of dictionary. Key is identified via the key field."""
-	for param in response:
-		if param[key_field] == key:
-			return param["Value"]
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
deleted file mode 100644
index b526624..0000000
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
+++ /dev/null
@@ -1,361 +0,0 @@
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import unittest
-from json import dumps
-
-import frappe
-
-from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
-from erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings import (
-	process_balance_info,
-	verify_transaction,
-)
-from erpnext.erpnext_integrations.utils import create_mode_of_payment
-
-
-class TestMpesaSettings(unittest.TestCase):
-	def setUp(self):
-		# create payment gateway in setup
-		create_mpesa_settings(payment_gateway_name="_Test")
-		create_mpesa_settings(payment_gateway_name="_Account Balance")
-		create_mpesa_settings(payment_gateway_name="Payment")
-
-	def tearDown(self):
-		frappe.db.sql("delete from `tabMpesa Settings`")
-		frappe.db.sql("delete from `tabIntegration Request` where integration_request_service = 'Mpesa'")
-
-	def test_creation_of_payment_gateway(self):
-		mode_of_payment = create_mode_of_payment("Mpesa-_Test", payment_type="Phone")
-		self.assertTrue(frappe.db.exists("Payment Gateway Account", {"payment_gateway": "Mpesa-_Test"}))
-		self.assertTrue(mode_of_payment.name)
-		self.assertEqual(mode_of_payment.type, "Phone")
-
-	def test_processing_of_account_balance(self):
-		mpesa_doc = create_mpesa_settings(payment_gateway_name="_Account Balance")
-		mpesa_doc.get_account_balance_info()
-
-		callback_response = get_account_balance_callback_payload()
-		process_balance_info(**callback_response)
-		integration_request = frappe.get_doc("Integration Request", "AG_20200927_00007cdb1f9fb6494315")
-
-		# test integration request creation and successful update of the status on receiving callback response
-		self.assertTrue(integration_request)
-		self.assertEqual(integration_request.status, "Completed")
-
-		# test formatting of account balance received as string to json with appropriate currency symbol
-		mpesa_doc.reload()
-		self.assertEqual(
-			mpesa_doc.account_balance,
-			dumps(
-				{
-					"Working Account": {
-						"current_balance": "Sh 481,000.00",
-						"available_balance": "Sh 481,000.00",
-						"reserved_balance": "Sh 0.00",
-						"uncleared_balance": "Sh 0.00",
-					}
-				}
-			),
-		)
-
-		integration_request.delete()
-
-	def test_processing_of_callback_payload(self):
-		mpesa_account = frappe.db.get_value(
-			"Payment Gateway Account", {"payment_gateway": "Mpesa-Payment"}, "payment_account"
-		)
-		frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
-		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES")
-
-		pos_invoice = create_pos_invoice(do_not_submit=1)
-		pos_invoice.append(
-			"payments", {"mode_of_payment": "Mpesa-Payment", "account": mpesa_account, "amount": 500}
-		)
-		pos_invoice.contact_mobile = "093456543894"
-		pos_invoice.currency = "KES"
-		pos_invoice.save()
-
-		pr = pos_invoice.create_payment_request()
-		# test payment request creation
-		self.assertEqual(pr.payment_gateway, "Mpesa-Payment")
-
-		# submitting payment request creates integration requests with random id
-		integration_req_ids = frappe.get_all(
-			"Integration Request",
-			filters={
-				"reference_doctype": pr.doctype,
-				"reference_docname": pr.name,
-			},
-			pluck="name",
-		)
-
-		callback_response = get_payment_callback_payload(
-			Amount=500, CheckoutRequestID=integration_req_ids[0]
-		)
-		verify_transaction(**callback_response)
-		# test creation of integration request
-		integration_request = frappe.get_doc("Integration Request", integration_req_ids[0])
-
-		# test integration request creation and successful update of the status on receiving callback response
-		self.assertTrue(integration_request)
-		self.assertEqual(integration_request.status, "Completed")
-
-		pos_invoice.reload()
-		integration_request.reload()
-		self.assertEqual(pos_invoice.mpesa_receipt_number, "LGR7OWQX0R")
-		self.assertEqual(integration_request.status, "Completed")
-
-		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "")
-		integration_request.delete()
-		pr.reload()
-		pr.cancel()
-		pr.delete()
-		pos_invoice.delete()
-
-	def test_processing_of_multiple_callback_payload(self):
-		mpesa_account = frappe.db.get_value(
-			"Payment Gateway Account", {"payment_gateway": "Mpesa-Payment"}, "payment_account"
-		)
-		frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
-		frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500")
-		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES")
-
-		pos_invoice = create_pos_invoice(do_not_submit=1)
-		pos_invoice.append(
-			"payments", {"mode_of_payment": "Mpesa-Payment", "account": mpesa_account, "amount": 1000}
-		)
-		pos_invoice.contact_mobile = "093456543894"
-		pos_invoice.currency = "KES"
-		pos_invoice.save()
-
-		pr = pos_invoice.create_payment_request()
-		# test payment request creation
-		self.assertEqual(pr.payment_gateway, "Mpesa-Payment")
-
-		# submitting payment request creates integration requests with random id
-		integration_req_ids = frappe.get_all(
-			"Integration Request",
-			filters={
-				"reference_doctype": pr.doctype,
-				"reference_docname": pr.name,
-			},
-			pluck="name",
-		)
-
-		# create random receipt nos and send it as response to callback handler
-		mpesa_receipt_numbers = [frappe.utils.random_string(5) for d in integration_req_ids]
-
-		integration_requests = []
-		for i in range(len(integration_req_ids)):
-			callback_response = get_payment_callback_payload(
-				Amount=500,
-				CheckoutRequestID=integration_req_ids[i],
-				MpesaReceiptNumber=mpesa_receipt_numbers[i],
-			)
-			# handle response manually
-			verify_transaction(**callback_response)
-			# test completion of integration request
-			integration_request = frappe.get_doc("Integration Request", integration_req_ids[i])
-			self.assertEqual(integration_request.status, "Completed")
-			integration_requests.append(integration_request)
-
-		# check receipt number once all the integration requests are completed
-		pos_invoice.reload()
-		self.assertEqual(pos_invoice.mpesa_receipt_number, ", ".join(mpesa_receipt_numbers))
-
-		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "")
-		[d.delete() for d in integration_requests]
-		pr.reload()
-		pr.cancel()
-		pr.delete()
-		pos_invoice.delete()
-
-	def test_processing_of_only_one_succes_callback_payload(self):
-		mpesa_account = frappe.db.get_value(
-			"Payment Gateway Account", {"payment_gateway": "Mpesa-Payment"}, "payment_account"
-		)
-		frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
-		frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500")
-		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES")
-
-		pos_invoice = create_pos_invoice(do_not_submit=1)
-		pos_invoice.append(
-			"payments", {"mode_of_payment": "Mpesa-Payment", "account": mpesa_account, "amount": 1000}
-		)
-		pos_invoice.contact_mobile = "093456543894"
-		pos_invoice.currency = "KES"
-		pos_invoice.save()
-
-		pr = pos_invoice.create_payment_request()
-		# test payment request creation
-		self.assertEqual(pr.payment_gateway, "Mpesa-Payment")
-
-		# submitting payment request creates integration requests with random id
-		integration_req_ids = frappe.get_all(
-			"Integration Request",
-			filters={
-				"reference_doctype": pr.doctype,
-				"reference_docname": pr.name,
-			},
-			pluck="name",
-		)
-
-		# create random receipt nos and send it as response to callback handler
-		mpesa_receipt_numbers = [frappe.utils.random_string(5) for d in integration_req_ids]
-
-		callback_response = get_payment_callback_payload(
-			Amount=500,
-			CheckoutRequestID=integration_req_ids[0],
-			MpesaReceiptNumber=mpesa_receipt_numbers[0],
-		)
-		# handle response manually
-		verify_transaction(**callback_response)
-		# test completion of integration request
-		integration_request = frappe.get_doc("Integration Request", integration_req_ids[0])
-		self.assertEqual(integration_request.status, "Completed")
-
-		# now one request is completed
-		# second integration request fails
-		# now retrying payment request should make only one integration request again
-		pr = pos_invoice.create_payment_request()
-		new_integration_req_ids = frappe.get_all(
-			"Integration Request",
-			filters={
-				"reference_doctype": pr.doctype,
-				"reference_docname": pr.name,
-				"name": ["not in", integration_req_ids],
-			},
-			pluck="name",
-		)
-
-		self.assertEqual(len(new_integration_req_ids), 1)
-
-		frappe.db.set_value("Customer", "_Test Customer", "default_currency", "")
-		frappe.db.sql("delete from `tabIntegration Request` where integration_request_service = 'Mpesa'")
-		pr.reload()
-		pr.cancel()
-		pr.delete()
-		pos_invoice.delete()
-
-
-def create_mpesa_settings(payment_gateway_name="Express"):
-	if frappe.db.exists("Mpesa Settings", payment_gateway_name):
-		return frappe.get_doc("Mpesa Settings", payment_gateway_name)
-
-	doc = frappe.get_doc(
-		dict(  # nosec
-			doctype="Mpesa Settings",
-			sandbox=1,
-			payment_gateway_name=payment_gateway_name,
-			consumer_key="5sMu9LVI1oS3oBGPJfh3JyvLHwZOdTKn",
-			consumer_secret="VI1oS3oBGPJfh3JyvLHw",
-			online_passkey="LVI1oS3oBGPJfh3JyvLHwZOd",
-			till_number="174379",
-		)
-	)
-
-	doc.insert(ignore_permissions=True)
-	return doc
-
-
-def get_test_account_balance_response():
-	"""Response received after calling the account balance API."""
-	return {
-		"ResultType": 0,
-		"ResultCode": 0,
-		"ResultDesc": "The service request has been accepted successfully.",
-		"OriginatorConversationID": "10816-694520-2",
-		"ConversationID": "AG_20200927_00007cdb1f9fb6494315",
-		"TransactionID": "LGR0000000",
-		"ResultParameters": {
-			"ResultParameter": [
-				{"Key": "ReceiptNo", "Value": "LGR919G2AV"},
-				{"Key": "Conversation ID", "Value": "AG_20170727_00004492b1b6d0078fbe"},
-				{"Key": "FinalisedTime", "Value": 20170727101415},
-				{"Key": "Amount", "Value": 10},
-				{"Key": "TransactionStatus", "Value": "Completed"},
-				{"Key": "ReasonType", "Value": "Salary Payment via API"},
-				{"Key": "TransactionReason"},
-				{"Key": "DebitPartyCharges", "Value": "Fee For B2C Payment|KES|33.00"},
-				{"Key": "DebitAccountType", "Value": "Utility Account"},
-				{"Key": "InitiatedTime", "Value": 20170727101415},
-				{"Key": "Originator Conversation ID", "Value": "19455-773836-1"},
-				{"Key": "CreditPartyName", "Value": "254708374149 - John Doe"},
-				{"Key": "DebitPartyName", "Value": "600134 - Safaricom157"},
-			]
-		},
-		"ReferenceData": {"ReferenceItem": {"Key": "Occasion", "Value": "aaaa"}},
-	}
-
-
-def get_payment_request_response_payload(Amount=500):
-	"""Response received after successfully calling the stk push process request API."""
-
-	CheckoutRequestID = frappe.utils.random_string(10)
-
-	return {
-		"MerchantRequestID": "8071-27184008-1",
-		"CheckoutRequestID": CheckoutRequestID,
-		"ResultCode": 0,
-		"ResultDesc": "The service request is processed successfully.",
-		"CallbackMetadata": {
-			"Item": [
-				{"Name": "Amount", "Value": Amount},
-				{"Name": "MpesaReceiptNumber", "Value": "LGR7OWQX0R"},
-				{"Name": "TransactionDate", "Value": 20201006113336},
-				{"Name": "PhoneNumber", "Value": 254723575670},
-			]
-		},
-	}
-
-
-def get_payment_callback_payload(
-	Amount=500, CheckoutRequestID="ws_CO_061020201133231972", MpesaReceiptNumber="LGR7OWQX0R"
-):
-	"""Response received from the server as callback after calling the stkpush process request API."""
-	return {
-		"Body": {
-			"stkCallback": {
-				"MerchantRequestID": "19465-780693-1",
-				"CheckoutRequestID": CheckoutRequestID,
-				"ResultCode": 0,
-				"ResultDesc": "The service request is processed successfully.",
-				"CallbackMetadata": {
-					"Item": [
-						{"Name": "Amount", "Value": Amount},
-						{"Name": "MpesaReceiptNumber", "Value": MpesaReceiptNumber},
-						{"Name": "Balance"},
-						{"Name": "TransactionDate", "Value": 20170727154800},
-						{"Name": "PhoneNumber", "Value": 254721566839},
-					]
-				},
-			}
-		}
-	}
-
-
-def get_account_balance_callback_payload():
-	"""Response received from the server as callback after calling the account balance API."""
-	return {
-		"Result": {
-			"ResultType": 0,
-			"ResultCode": 0,
-			"ResultDesc": "The service request is processed successfully.",
-			"OriginatorConversationID": "16470-170099139-1",
-			"ConversationID": "AG_20200927_00007cdb1f9fb6494315",
-			"TransactionID": "OIR0000000",
-			"ResultParameters": {
-				"ResultParameter": [
-					{"Key": "AccountBalance", "Value": "Working Account|KES|481000.00|481000.00|0.00|0.00"},
-					{"Key": "BOCompletedTime", "Value": 20200927234123},
-				]
-			},
-			"ReferenceData": {
-				"ReferenceItem": {
-					"Key": "QueueTimeoutURL",
-					"Value": "https://internalsandbox.safaricom.co.ke/mpesa/abresults/v1/submit",
-				}
-			},
-		}
-	}
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
index 11d5f6a..eb99345 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
@@ -7,7 +7,7 @@
 from frappe import _
 from frappe.desk.doctype.tag.tag import add_tag
 from frappe.model.document import Document
-from frappe.utils import add_months, formatdate, getdate, today
+from frappe.utils import add_months, formatdate, getdate, sbool, today
 from plaid.errors import ItemError
 
 from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
@@ -237,8 +237,6 @@
 		deposit = abs(amount)
 		withdrawal = 0.0
 
-	status = "Pending" if transaction["pending"] == True else "Settled"
-
 	tags = []
 	if transaction["category"]:
 		try:
@@ -247,13 +245,14 @@
 		except KeyError:
 			pass
 
-	if not frappe.db.exists("Bank Transaction", dict(transaction_id=transaction["transaction_id"])):
+	if not frappe.db.exists(
+		"Bank Transaction", dict(transaction_id=transaction["transaction_id"])
+	) and not sbool(transaction["pending"]):
 		try:
 			new_transaction = frappe.get_doc(
 				{
 					"doctype": "Bank Transaction",
 					"date": getdate(transaction["date"]),
-					"status": status,
 					"bank_account": bank_account,
 					"deposit": deposit,
 					"withdrawal": withdrawal,
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py
index 86e1b31..6716853 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py
@@ -43,40 +43,6 @@
 		add_account_subtype("loan")
 		self.assertEqual(frappe.get_doc("Bank Account Subtype", "loan").name, "loan")
 
-	def test_default_bank_account(self):
-		if not frappe.db.exists("Bank", "Citi"):
-			frappe.get_doc({"doctype": "Bank", "bank_name": "Citi"}).insert()
-
-		bank_accounts = {
-			"account": {
-				"subtype": "checking",
-				"mask": "0000",
-				"type": "depository",
-				"id": "6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK",
-				"name": "Plaid Checking",
-			},
-			"account_id": "6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK",
-			"link_session_id": "db673d75-61aa-442a-864f-9b3f174f3725",
-			"accounts": [
-				{
-					"type": "depository",
-					"subtype": "checking",
-					"mask": "0000",
-					"id": "6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK",
-					"name": "Plaid Checking",
-				}
-			],
-			"institution": {"institution_id": "ins_6", "name": "Citi"},
-		}
-
-		bank = json.dumps(frappe.get_doc("Bank", "Citi").as_dict(), default=json_handler)
-		company = frappe.db.get_single_value("Global Defaults", "default_company")
-		frappe.db.set_value("Company", company, "default_bank_account", None)
-
-		self.assertRaises(
-			frappe.ValidationError, add_bank_accounts, response=bank_accounts, bank=bank, company=company
-		)
-
 	def test_new_transaction(self):
 		if not frappe.db.exists("Bank", "Citi"):
 			frappe.get_doc({"doctype": "Bank", "bank_name": "Citi"}).insert()
diff --git a/erpnext/erpnext_integrations/stripe_integration.py b/erpnext/erpnext_integrations/stripe_integration.py
deleted file mode 100644
index 634e5c2..0000000
--- a/erpnext/erpnext_integrations/stripe_integration.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-import frappe
-from frappe import _
-from frappe.integrations.utils import create_request_log
-
-from erpnext.utilities import payment_app_import_guard
-
-
-def create_stripe_subscription(gateway_controller, data):
-	with payment_app_import_guard():
-		import stripe
-
-	stripe_settings = frappe.get_doc("Stripe Settings", gateway_controller)
-	stripe_settings.data = frappe._dict(data)
-
-	stripe.api_key = stripe_settings.get_password(fieldname="secret_key", raise_exception=False)
-	stripe.default_http_client = stripe.http_client.RequestsClient()
-
-	try:
-		stripe_settings.integration_request = create_request_log(stripe_settings.data, "Host", "Stripe")
-		stripe_settings.payment_plans = frappe.get_doc(
-			"Payment Request", stripe_settings.data.reference_docname
-		).subscription_plans
-		return create_subscription_on_stripe(stripe_settings)
-
-	except Exception:
-		stripe_settings.log_error("Unable to create Stripe subscription")
-		return {
-			"redirect_to": frappe.redirect_to_message(
-				_("Server Error"),
-				_(
-					"It seems that there is an issue with the server's stripe configuration. In case of failure, the amount will get refunded to your account."
-				),
-			),
-			"status": 401,
-		}
-
-
-def create_subscription_on_stripe(stripe_settings):
-	with payment_app_import_guard():
-		import stripe
-
-	items = []
-	for payment_plan in stripe_settings.payment_plans:
-		plan = frappe.db.get_value("Subscription Plan", payment_plan.plan, "product_price_id")
-		items.append({"price": plan, "quantity": payment_plan.qty})
-
-	try:
-		customer = stripe.Customer.create(
-			source=stripe_settings.data.stripe_token_id,
-			description=stripe_settings.data.payer_name,
-			email=stripe_settings.data.payer_email,
-		)
-
-		subscription = stripe.Subscription.create(customer=customer, items=items)
-
-		if subscription.status == "active":
-			stripe_settings.integration_request.db_set("status", "Completed", update_modified=False)
-			stripe_settings.flags.status_changed_to = "Completed"
-
-		else:
-			stripe_settings.integration_request.db_set("status", "Failed", update_modified=False)
-			frappe.log_error(f"Stripe Subscription ID {subscription.id}: Payment failed")
-	except Exception:
-		stripe_settings.integration_request.db_set("status", "Failed", update_modified=False)
-		stripe_settings.log_error("Unable to create Stripe subscription")
-
-	return stripe_settings.finalize_request()
diff --git a/erpnext/erpnext_integrations/utils.py b/erpnext/erpnext_integrations/utils.py
index 981486e..8984f1b 100644
--- a/erpnext/erpnext_integrations/utils.py
+++ b/erpnext/erpnext_integrations/utils.py
@@ -6,8 +6,6 @@
 import frappe
 from frappe import _
 
-from erpnext import get_default_company
-
 
 def validate_webhooks_request(doctype, hmac_key, secret_key="secret"):
 	def innerfn(fn):
@@ -47,35 +45,6 @@
 	return server_url
 
 
-def create_mode_of_payment(gateway, payment_type="General"):
-	payment_gateway_account = frappe.db.get_value(
-		"Payment Gateway Account", {"payment_gateway": gateway}, ["payment_account"]
-	)
-
-	mode_of_payment = frappe.db.exists("Mode of Payment", gateway)
-	if not mode_of_payment and payment_gateway_account:
-		mode_of_payment = frappe.get_doc(
-			{
-				"doctype": "Mode of Payment",
-				"mode_of_payment": gateway,
-				"enabled": 1,
-				"type": payment_type,
-				"accounts": [
-					{
-						"doctype": "Mode of Payment Account",
-						"company": get_default_company(),
-						"default_account": payment_gateway_account,
-					}
-				],
-			}
-		)
-		mode_of_payment.insert(ignore_permissions=True)
-
-		return mode_of_payment
-	elif mode_of_payment:
-		return frappe.get_doc("Mode of Payment", mode_of_payment)
-
-
 def get_tracking_url(carrier, tracking_number):
 	# Return the formatted Tracking URL.
 	tracking_url = ""
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 2155699..5483a10 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -52,11 +52,7 @@
 filters_config = "erpnext.startup.filters.get_filters_config"
 additional_print_settings = "erpnext.controllers.print_settings.get_print_settings"
 
-on_session_creation = [
-	"erpnext.portal.utils.create_customer_or_supplier",
-	"erpnext.e_commerce.shopping_cart.utils.set_cart_count",
-]
-on_logout = "erpnext.e_commerce.shopping_cart.utils.clear_cart_count"
+on_session_creation = "erpnext.portal.utils.create_customer_or_supplier"
 
 treeviews = [
 	"Account",
@@ -90,15 +86,11 @@
 }
 
 # website
-update_website_context = [
-	"erpnext.e_commerce.shopping_cart.utils.update_website_context",
-]
-my_account_context = "erpnext.e_commerce.shopping_cart.utils.update_my_account_context"
 webform_list_context = "erpnext.controllers.website_list_for_contact.get_webform_list_context"
 
 calendars = ["Task", "Work Order", "Sales Order", "Holiday List", "ToDo"]
 
-website_generators = ["Item Group", "Website Item", "BOM", "Sales Partner"]
+website_generators = ["BOM", "Sales Partner"]
 
 website_context = {
 	"favicon": "/assets/erpnext/images/erpnext-favicon.svg",
@@ -349,9 +341,6 @@
 	"Event": {
 		"after_insert": "erpnext.crm.utils.link_events_with_prospect",
 	},
-	"Sales Taxes and Charges Template": {
-		"on_update": "erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings.validate_cart_settings"
-	},
 	"Sales Invoice": {
 		"on_submit": [
 			"erpnext.regional.create_transaction_log",
@@ -519,6 +508,7 @@
 	"Sales Invoice Item",
 	"Purchase Invoice Item",
 	"Purchase Order Item",
+	"Sales Order Item",
 	"Journal Entry Account",
 	"Material Request Item",
 	"Delivery Note Item",
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
index 32f1c36..0135a4f 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
@@ -107,7 +107,7 @@
 			allowance = flt(
 				frappe.db.get_single_value(
 					"Selling Settings" if order_doc.doctype == "Sales Order" else "Buying Settings",
-					"over_order_allowance",
+					"blanket_order_allowance",
 				)
 			)
 			for bo_name, item_data in order_data.items():
diff --git a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
index 58f3c95..e9fc25b 100644
--- a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
+++ b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
@@ -63,7 +63,7 @@
 		po1.currency = get_company_currency(po1.company)
 		self.assertEqual(po1.items[0].qty, (bo.items[0].qty - bo.items[0].ordered_qty))
 
-	def test_over_order_allowance(self):
+	def test_blanket_order_allowance(self):
 		# Sales Order
 		bo = make_blanket_order(blanket_order_type="Selling", quantity=100)
 
@@ -74,7 +74,7 @@
 		so.items[0].qty = 110
 		self.assertRaises(frappe.ValidationError, so.submit)
 
-		frappe.db.set_single_value("Selling Settings", "over_order_allowance", 10)
+		frappe.db.set_single_value("Selling Settings", "blanket_order_allowance", 10)
 		so.submit()
 
 		# Purchase Order
@@ -87,7 +87,7 @@
 		po.items[0].qty = 110
 		self.assertRaises(frappe.ValidationError, po.submit)
 
-		frappe.db.set_single_value("Buying Settings", "over_order_allowance", 10)
+		frappe.db.set_single_value("Buying Settings", "blanket_order_allowance", 10)
 		po.submit()
 
 
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 0231668..229f885 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -1196,12 +1196,12 @@
 def add_additional_cost(stock_entry, work_order):
 	# Add non stock items cost in the additional cost
 	stock_entry.additional_costs = []
-	expenses_included_in_valuation = frappe.get_cached_value(
-		"Company", work_order.company, "expenses_included_in_valuation"
+	default_expense_account = frappe.get_cached_value(
+		"Company", work_order.company, "default_expense_account"
 	)
 
-	add_non_stock_items_cost(stock_entry, work_order, expenses_included_in_valuation)
-	add_operations_cost(stock_entry, work_order, expenses_included_in_valuation)
+	add_non_stock_items_cost(stock_entry, work_order, default_expense_account)
+	add_operations_cost(stock_entry, work_order, default_expense_account)
 
 
 def add_non_stock_items_cost(stock_entry, work_order, expense_account):
diff --git a/erpnext/manufacturing/doctype/job_card/job_card_calendar.js b/erpnext/manufacturing/doctype/job_card/job_card_calendar.js
index f4877fd..9e32085 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card_calendar.js
+++ b/erpnext/manufacturing/doctype/job_card/job_card_calendar.js
@@ -10,8 +10,8 @@
 	},
 	gantt: {
 		field_map: {
-			"start": "started_time",
-			"end": "started_time",
+			"start": "expected_start_date",
+			"end": "expected_end_date",
 			"id": "name",
 			"title": "subject",
 			"color": "color",
diff --git a/erpnext/manufacturing/doctype/job_card/job_card_list.js b/erpnext/manufacturing/doctype/job_card/job_card_list.js
index 5d883bf..99fca95 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card_list.js
+++ b/erpnext/manufacturing/doctype/job_card/job_card_list.js
@@ -1,6 +1,6 @@
 frappe.listview_settings['Job Card'] = {
 	has_indicator_for_draft: true,
-
+	add_fields: ["expected_start_date", "expected_end_date"],
 	get_indicator: function(doc) {
 		const status_colors = {
 			"Work In Progress": "orange",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index deef020..ddd9375 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -8,7 +8,6 @@
 import frappe
 from frappe import _, msgprint
 from frappe.model.document import Document
-from frappe.query_builder import Case
 from frappe.query_builder.functions import IfNull, Sum
 from frappe.utils import (
 	add_days,
@@ -1618,21 +1617,13 @@
 	table = frappe.qb.DocType("Production Plan")
 	child = frappe.qb.DocType("Material Request Plan Item")
 
-	completed_production_plans = get_completed_production_plans()
+	non_completed_production_plans = get_non_completed_production_plans()
 
-	case = Case()
 	query = (
 		frappe.qb.from_(table)
 		.inner_join(child)
 		.on(table.name == child.parent)
-		.select(
-			Sum(
-				child.quantity
-				* IfNull(
-					case.when(child.material_request_type == "Purchase", child.conversion_factor).else_(1.0), 1.0
-				)
-			)
-		)
+		.select(Sum(child.required_bom_qty))
 		.where(
 			(table.docstatus == 1)
 			& (child.item_code == item_code)
@@ -1641,8 +1632,8 @@
 		)
 	)
 
-	if completed_production_plans:
-		query = query.where(table.name.notin(completed_production_plans))
+	if non_completed_production_plans:
+		query = query.where(table.name.isin(non_completed_production_plans))
 
 	query = query.run()
 
@@ -1653,7 +1644,7 @@
 
 	reserved_qty_for_production = flt(
 		get_reserved_qty_for_production(
-			item_code, warehouse, completed_production_plans, check_production_plan=True
+			item_code, warehouse, non_completed_production_plans, check_production_plan=True
 		)
 	)
 
@@ -1663,7 +1654,7 @@
 	return reserved_qty_for_production_plan - reserved_qty_for_production
 
 
-def get_completed_production_plans():
+def get_non_completed_production_plans():
 	table = frappe.qb.DocType("Production Plan")
 	child = frappe.qb.DocType("Production Plan Item")
 
@@ -1675,7 +1666,7 @@
 		.where(
 			(table.docstatus == 1)
 			& (table.status.notin(["Completed", "Closed"]))
-			& (child.ordered_qty >= child.planned_qty)
+			& (child.planned_qty > child.ordered_qty)
 		)
 	).run(as_dict=True)
 
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index 4ff9d29..6ab9232 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -6,8 +6,8 @@
 
 from erpnext.controllers.item_variant import create_variant
 from erpnext.manufacturing.doctype.production_plan.production_plan import (
-	get_completed_production_plans,
 	get_items_for_material_requests,
+	get_non_completed_production_plans,
 	get_sales_orders,
 	get_warehouse_list,
 )
@@ -1143,9 +1143,9 @@
 
 		self.assertEqual(after_qty, before_qty)
 
-		completed_plans = get_completed_production_plans()
+		completed_plans = get_non_completed_production_plans()
 		for plan in plans:
-			self.assertTrue(plan in completed_plans)
+			self.assertFalse(plan in completed_plans)
 
 	def test_resered_qty_for_production_plan_for_material_requests_with_multi_UOM(self):
 		from erpnext.stock.utils import get_or_make_bin
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 3dc33ac..f9fddcb 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -1515,7 +1515,7 @@
 def get_reserved_qty_for_production(
 	item_code: str,
 	warehouse: str,
-	completed_production_plans: list = None,
+	non_completed_production_plans: list = None,
 	check_production_plan: bool = False,
 ) -> float:
 	"""Get total reserved quantity for any item in specified warehouse"""
@@ -1538,19 +1538,22 @@
 			& (wo_item.parent == wo.name)
 			& (wo.docstatus == 1)
 			& (wo_item.source_warehouse == warehouse)
-			& (wo.status.notin(["Stopped", "Completed", "Closed"]))
-			& (
-				(wo_item.required_qty > wo_item.transferred_qty)
-				| (wo_item.required_qty > wo_item.consumed_qty)
-			)
 		)
 	)
 
 	if check_production_plan:
 		query = query.where(wo.production_plan.isnotnull())
+	else:
+		query = query.where(
+			(wo.status.notin(["Stopped", "Completed", "Closed"]))
+			& (
+				(wo_item.required_qty > wo_item.transferred_qty)
+				| (wo_item.required_qty > wo_item.consumed_qty)
+			)
+		)
 
-	if completed_production_plans:
-		query = query.where(wo.production_plan.notin(completed_production_plans))
+	if non_completed_production_plans:
+		query = query.where(wo.production_plan.isin(non_completed_production_plans))
 
 	return query.run()[0][0] or 0.0
 
diff --git a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js
index 34edb9d..8729775 100644
--- a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js
+++ b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js
@@ -12,7 +12,7 @@
 			"options": "Item",
 			"get_query": () =>{
 				return {
-					filters: { "disabled": 0, "is_stock_item": 1 }
+					filters: { "is_stock_item": 1 }
 				}
 			}
 		},
diff --git a/erpnext/modules.txt b/erpnext/modules.txt
index dcb4212..c53cdf4 100644
--- a/erpnext/modules.txt
+++ b/erpnext/modules.txt
@@ -17,5 +17,4 @@
 Communication
 Telephony
 Bulk Transaction
-E-commerce
-Subcontracting
\ No newline at end of file
+Subcontracting
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 4c7d8e5..3f9744d 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -223,9 +223,6 @@
 erpnext.patches.v13_0.set_operation_time_based_on_operating_cost
 erpnext.patches.v13_0.create_gst_payment_entry_fields #27-11-2021
 erpnext.patches.v13_0.fix_invoice_statuses
-erpnext.patches.v13_0.create_website_items #30-09-2021
-erpnext.patches.v13_0.populate_e_commerce_settings
-erpnext.patches.v13_0.make_homepage_products_website_items
 erpnext.patches.v13_0.replace_supplier_item_group_with_party_specific_item
 erpnext.patches.v13_0.update_dates_in_tax_withholding_category
 erpnext.patches.v14_0.update_opportunity_currency_fields
@@ -242,7 +239,6 @@
 erpnext.patches.v13_0.healthcare_deprecation_warning
 erpnext.patches.v13_0.item_naming_series_not_mandatory
 erpnext.patches.v13_0.update_category_in_ltds_certificate
-erpnext.patches.v13_0.fetch_thumbnail_in_website_items
 erpnext.patches.v13_0.update_maintenance_schedule_field_in_visit
 erpnext.patches.v14_0.migrate_crm_settings
 erpnext.patches.v13_0.wipe_serial_no_field_for_0_qty
@@ -257,6 +253,7 @@
 erpnext.patches.v13_0.reset_corrupt_defaults
 erpnext.patches.v13_0.create_accounting_dimensions_for_asset_repair
 erpnext.patches.v15_0.delete_taxjar_doctypes
+erpnext.patches.v15_0.delete_ecommerce_doctypes
 erpnext.patches.v15_0.create_asset_depreciation_schedules_from_assets
 erpnext.patches.v14_0.update_reference_due_date_in_journal_entry
 erpnext.patches.v15_0.saudi_depreciation_warning
@@ -277,8 +274,6 @@
 erpnext.patches.v14_0.rearrange_company_fields
 erpnext.patches.v13_0.update_sane_transfer_against
 erpnext.patches.v14_0.migrate_cost_center_allocations
-erpnext.patches.v13_0.convert_to_website_item_in_item_card_group_template
-erpnext.patches.v13_0.shopping_cart_to_ecommerce
 erpnext.patches.v13_0.update_reserved_qty_closed_wo
 erpnext.patches.v13_0.update_exchange_rate_settings
 erpnext.patches.v14_0.delete_amazon_mws_doctype
@@ -288,7 +283,6 @@
 erpnext.patches.v14_0.delete_non_profit_doctypes
 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.copy_custom_field_filters_to_website_item
 erpnext.patches.v13_0.change_default_item_manufacturer_fieldtype
 erpnext.patches.v13_0.requeue_recoverable_reposts
 erpnext.patches.v14_0.discount_accounting_separation
@@ -322,7 +316,7 @@
 execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0)
 erpnext.patches.v14_0.update_reference_type_in_journal_entry_accounts
 erpnext.patches.v14_0.update_subscription_details
-execute:frappe.delete_doc_if_exists("Report", "Tax Detail")
+execute:frappe.delete_doc("Report", "Tax Detail", force=True)
 erpnext.patches.v15_0.enable_all_leads
 erpnext.patches.v14_0.update_company_in_ldc
 erpnext.patches.v14_0.set_packed_qty_in_draft_delivery_notes
@@ -344,6 +338,11 @@
 erpnext.patches.v14_0.migrate_deferred_accounts_to_item_defaults
 erpnext.patches.v14_0.update_invoicing_period_in_subscription
 execute:frappe.delete_doc("Page", "welcome-to-erpnext")
+erpnext.patches.v15_0.delete_payment_gateway_doctypes
+erpnext.patches.v14_0.create_accounting_dimensions_in_sales_order_item
+erpnext.patches.v15_0.update_sre_from_voucher_details
+erpnext.patches.v14_0.rename_over_order_allowance_field
+erpnext.patches.v14_0.migrate_delivery_stop_lock_field
 erpnext.patches.v15_0.create_advance_payment_status
 # below migration patch should always run last
 erpnext.patches.v14_0.migrate_gl_to_payment_ledger
diff --git a/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py b/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py
deleted file mode 100644
index 9588e02..0000000
--- a/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py
+++ /dev/null
@@ -1,42 +0,0 @@
-import frappe
-
-
-def execute():
-	frappe.reload_doctype("Landed Cost Taxes and Charges")
-
-	company_account_map = frappe._dict(
-		frappe.db.sql(
-			"""
-		SELECT name, expenses_included_in_valuation from `tabCompany`
-	"""
-		)
-	)
-
-	for company, account in company_account_map.items():
-		frappe.db.sql(
-			"""
-			UPDATE
-				`tabLanded Cost Taxes and Charges` t, `tabLanded Cost Voucher` l
-			SET
-				t.expense_account = %s
-			WHERE
-				l.docstatus = 1
-				AND l.company = %s
-				AND t.parent = l.name
-		""",
-			(account, company),
-		)
-
-		frappe.db.sql(
-			"""
-			UPDATE
-				`tabLanded Cost Taxes and Charges` t, `tabStock Entry` s
-			SET
-				t.expense_account = %s
-			WHERE
-				s.docstatus = 1
-				AND s.company = %s
-				AND t.parent = s.name
-		""",
-			(account, company),
-		)
diff --git a/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py b/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py
index efbb96c..e53bdf8 100644
--- a/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py
+++ b/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py
@@ -3,23 +3,24 @@
 
 def execute():
 	frappe.reload_doc("stock", "doctype", "quality_inspection_parameter")
+	params = set()
 
-	# get all distinct parameters from QI readigs table
-	reading_params = frappe.db.get_all(
-		"Quality Inspection Reading", fields=["distinct specification"]
-	)
-	reading_params = [d.specification for d in reading_params]
+	# get all parameters from QI readings table
+	for (p,) in frappe.db.get_all(
+		"Quality Inspection Reading", fields=["specification"], as_list=True
+	):
+		params.add(p.strip())
 
-	# get all distinct parameters from QI Template as some may be unused in QI
-	template_params = frappe.db.get_all(
-		"Item Quality Inspection Parameter", fields=["distinct specification"]
-	)
-	template_params = [d.specification for d in template_params]
+	# get all parameters from QI Template as some may be unused in QI
+	for (p,) in frappe.db.get_all(
+		"Item Quality Inspection Parameter", fields=["specification"], as_list=True
+	):
+		params.add(p.strip())
 
-	params = list(set(reading_params + template_params))
+	# because db primary keys are case insensitive, so duplicates will cause an exception
+	params = set({x.casefold(): x for x in params}.values())
 
 	for parameter in params:
-		if not frappe.db.exists("Quality Inspection Parameter", parameter):
-			frappe.get_doc(
-				{"doctype": "Quality Inspection Parameter", "parameter": parameter, "description": parameter}
-			).insert(ignore_permissions=True)
+		frappe.get_doc(
+			{"doctype": "Quality Inspection Parameter", "parameter": parameter, "description": parameter}
+		).insert(ignore_permissions=True)
diff --git a/erpnext/patches/v13_0/convert_to_website_item_in_item_card_group_template.py b/erpnext/patches/v13_0/convert_to_website_item_in_item_card_group_template.py
deleted file mode 100644
index 1bac0fd..0000000
--- a/erpnext/patches/v13_0/convert_to_website_item_in_item_card_group_template.py
+++ /dev/null
@@ -1,60 +0,0 @@
-import json
-from typing import List, Union
-
-import frappe
-
-from erpnext.e_commerce.doctype.website_item.website_item import make_website_item
-
-
-def execute():
-	"""
-	Convert all Item links to Website Item link values in
-	exisitng 'Item Card Group' Web Page Block data.
-	"""
-	frappe.reload_doc("e_commerce", "web_template", "item_card_group")
-
-	blocks = frappe.db.get_all(
-		"Web Page Block",
-		filters={"web_template": "Item Card Group"},
-		fields=["parent", "web_template_values", "name"],
-	)
-
-	fields = generate_fields_to_edit()
-
-	for block in blocks:
-		web_template_value = json.loads(block.get("web_template_values"))
-
-		for field in fields:
-			item = web_template_value.get(field)
-			if not item:
-				continue
-
-			if frappe.db.exists("Website Item", {"item_code": item}):
-				website_item = frappe.db.get_value("Website Item", {"item_code": item})
-			else:
-				website_item = make_new_website_item(item)
-
-			if website_item:
-				web_template_value[field] = website_item
-
-		frappe.db.set_value(
-			"Web Page Block", block.name, "web_template_values", json.dumps(web_template_value)
-		)
-
-
-def generate_fields_to_edit() -> List:
-	fields = []
-	for i in range(1, 13):
-		fields.append(f"card_{i}_item")  # fields like 'card_1_item', etc.
-
-	return fields
-
-
-def make_new_website_item(item: str) -> Union[str, None]:
-	try:
-		doc = frappe.get_doc("Item", item)
-		web_item = make_website_item(doc)  # returns [website_item.name, item_name]
-		return web_item[0]
-	except Exception:
-		doc.log_error("Website Item creation failed")
-		return None
diff --git a/erpnext/patches/v13_0/copy_custom_field_filters_to_website_item.py b/erpnext/patches/v13_0/copy_custom_field_filters_to_website_item.py
deleted file mode 100644
index 4ad572f..0000000
--- a/erpnext/patches/v13_0/copy_custom_field_filters_to_website_item.py
+++ /dev/null
@@ -1,94 +0,0 @@
-import frappe
-from frappe.custom.doctype.custom_field.custom_field import create_custom_field
-
-
-def execute():
-	"Add Field Filters, that are not standard fields in Website Item, as Custom Fields."
-
-	def move_table_multiselect_data(docfield):
-		"Copy child table data (Table Multiselect) from Item to Website Item for a docfield."
-		table_multiselect_data = get_table_multiselect_data(docfield)
-		field = docfield.fieldname
-
-		for row in table_multiselect_data:
-			# add copied multiselect data rows in Website Item
-			web_item = frappe.db.get_value("Website Item", {"item_code": row.parent})
-			web_item_doc = frappe.get_doc("Website Item", web_item)
-
-			child_doc = frappe.new_doc(docfield.options, parent_doc=web_item_doc, parentfield=field)
-
-			for field in ["name", "creation", "modified", "idx"]:
-				row[field] = None
-
-			child_doc.update(row)
-
-			child_doc.parenttype = "Website Item"
-			child_doc.parent = web_item
-
-			child_doc.insert()
-
-	def get_table_multiselect_data(docfield):
-		child_table = frappe.qb.DocType(docfield.options)
-		item = frappe.qb.DocType("Item")
-
-		table_multiselect_data = (  # query table data for field
-			frappe.qb.from_(child_table)
-			.join(item)
-			.on(item.item_code == child_table.parent)
-			.select(child_table.star)
-			.where((child_table.parentfield == docfield.fieldname) & (item.published_in_website == 1))
-		).run(as_dict=True)
-
-		return table_multiselect_data
-
-	settings = frappe.get_doc("E Commerce Settings")
-
-	if not (settings.enable_field_filters or settings.filter_fields):
-		return
-
-	item_meta = frappe.get_meta("Item")
-	valid_item_fields = [
-		df.fieldname for df in item_meta.fields if df.fieldtype in ["Link", "Table MultiSelect"]
-	]
-
-	web_item_meta = frappe.get_meta("Website Item")
-	valid_web_item_fields = [
-		df.fieldname for df in web_item_meta.fields if df.fieldtype in ["Link", "Table MultiSelect"]
-	]
-
-	for row in settings.filter_fields:
-		# skip if illegal field
-		if row.fieldname not in valid_item_fields:
-			continue
-
-		# if Item field is not in Website Item, add it as a custom field
-		if row.fieldname not in valid_web_item_fields:
-			df = item_meta.get_field(row.fieldname)
-			create_custom_field(
-				"Website Item",
-				dict(
-					owner="Administrator",
-					fieldname=df.fieldname,
-					label=df.label,
-					fieldtype=df.fieldtype,
-					options=df.options,
-					description=df.description,
-					read_only=df.read_only,
-					no_copy=df.no_copy,
-					insert_after="on_backorder",
-				),
-			)
-
-			# map field values
-			if df.fieldtype == "Table MultiSelect":
-				move_table_multiselect_data(df)
-			else:
-				frappe.db.sql(  # nosemgrep
-					"""
-						UPDATE `tabWebsite Item` wi, `tabItem` i
-						SET wi.{0} = i.{0}
-						WHERE wi.item_code = i.item_code
-					""".format(
-						row.fieldname
-					)
-				)
diff --git a/erpnext/patches/v13_0/create_website_items.py b/erpnext/patches/v13_0/create_website_items.py
deleted file mode 100644
index b010f0e..0000000
--- a/erpnext/patches/v13_0/create_website_items.py
+++ /dev/null
@@ -1,85 +0,0 @@
-import frappe
-
-from erpnext.e_commerce.doctype.website_item.website_item import make_website_item
-
-
-def execute():
-	frappe.reload_doc("e_commerce", "doctype", "website_item")
-	frappe.reload_doc("e_commerce", "doctype", "website_item_tabbed_section")
-	frappe.reload_doc("e_commerce", "doctype", "website_offer")
-	frappe.reload_doc("e_commerce", "doctype", "recommended_items")
-	frappe.reload_doc("e_commerce", "doctype", "e_commerce_settings")
-	frappe.reload_doc("stock", "doctype", "item")
-
-	item_fields = [
-		"item_code",
-		"item_name",
-		"item_group",
-		"stock_uom",
-		"brand",
-		"has_variants",
-		"variant_of",
-		"description",
-		"weightage",
-	]
-	web_fields_to_map = [
-		"route",
-		"slideshow",
-		"website_image_alt",
-		"website_warehouse",
-		"web_long_description",
-		"website_content",
-		"website_image",
-		"thumbnail",
-	]
-
-	# get all valid columns (fields) from Item master DB schema
-	item_table_fields = frappe.db.sql("desc `tabItem`", as_dict=1)  # nosemgrep
-	item_table_fields = [d.get("Field") for d in item_table_fields]
-
-	# prepare fields to query from Item, check if the web field exists in Item master
-	web_query_fields = []
-	for web_field in web_fields_to_map:
-		if web_field in item_table_fields:
-			web_query_fields.append(web_field)
-			item_fields.append(web_field)
-
-	# check if the filter fields exist in Item master
-	or_filters = {}
-	for field in ["show_in_website", "show_variant_in_website"]:
-		if field in item_table_fields:
-			or_filters[field] = 1
-
-	if not web_query_fields or not or_filters:
-		# web fields to map are not present in Item master schema
-		# most likely a fresh installation that doesnt need this patch
-		return
-
-	items = frappe.db.get_all("Item", fields=item_fields, or_filters=or_filters)
-	total_count = len(items)
-
-	for count, item in enumerate(items, start=1):
-		if frappe.db.exists("Website Item", {"item_code": item.item_code}):
-			continue
-
-		# make new website item from item (publish item)
-		website_item = make_website_item(item, save=False)
-		website_item.ranking = item.get("weightage")
-
-		for field in web_fields_to_map:
-			website_item.update({field: item.get(field)})
-
-		website_item.save()
-
-		# move Website Item Group & Website Specification table to Website Item
-		for doctype in ("Website Item Group", "Item Website Specification"):
-			frappe.db.set_value(
-				doctype,
-				{"parenttype": "Item", "parent": item.item_code},  # filters
-				{"parenttype": "Website Item", "parent": website_item.name},  # value dict
-			)
-
-		if count % 20 == 0:  # commit after every 20 items
-			frappe.db.commit()
-
-		frappe.utils.update_progress_bar("Creating Website Items", count, total_count)
diff --git a/erpnext/patches/v13_0/fetch_thumbnail_in_website_items.py b/erpnext/patches/v13_0/fetch_thumbnail_in_website_items.py
deleted file mode 100644
index 9197d86..0000000
--- a/erpnext/patches/v13_0/fetch_thumbnail_in_website_items.py
+++ /dev/null
@@ -1,11 +0,0 @@
-import frappe
-
-
-def execute():
-	if frappe.db.has_column("Item", "thumbnail"):
-		website_item = frappe.qb.DocType("Website Item").as_("wi")
-		item = frappe.qb.DocType("Item")
-
-		frappe.qb.update(website_item).inner_join(item).on(website_item.item_code == item.item_code).set(
-			website_item.thumbnail, item.thumbnail
-		).where(website_item.website_image.notnull() & website_item.thumbnail.isnull()).run()
diff --git a/erpnext/patches/v13_0/make_homepage_products_website_items.py b/erpnext/patches/v13_0/make_homepage_products_website_items.py
deleted file mode 100644
index 50bfd35..0000000
--- a/erpnext/patches/v13_0/make_homepage_products_website_items.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import frappe
-
-
-def execute():
-	homepage = frappe.get_doc("Homepage")
-
-	for row in homepage.products:
-		web_item = frappe.db.get_value("Website Item", {"item_code": row.item_code}, "name")
-		if not web_item:
-			continue
-
-		row.item_code = web_item
-
-	homepage.flags.ignore_mandatory = True
-	homepage.save()
diff --git a/erpnext/patches/v13_0/populate_e_commerce_settings.py b/erpnext/patches/v13_0/populate_e_commerce_settings.py
deleted file mode 100644
index ecf512b..0000000
--- a/erpnext/patches/v13_0/populate_e_commerce_settings.py
+++ /dev/null
@@ -1,68 +0,0 @@
-import frappe
-from frappe.utils import cint
-
-
-def execute():
-	frappe.reload_doc("e_commerce", "doctype", "e_commerce_settings")
-	frappe.reload_doc("portal", "doctype", "website_filter_field")
-	frappe.reload_doc("portal", "doctype", "website_attribute")
-
-	products_settings_fields = [
-		"hide_variants",
-		"products_per_page",
-		"enable_attribute_filters",
-		"enable_field_filters",
-	]
-
-	shopping_cart_settings_fields = [
-		"enabled",
-		"show_attachments",
-		"show_price",
-		"show_stock_availability",
-		"enable_variants",
-		"show_contact_us_button",
-		"show_quantity_in_website",
-		"show_apply_coupon_code_in_website",
-		"allow_items_not_in_stock",
-		"company",
-		"price_list",
-		"default_customer_group",
-		"quotation_series",
-		"enable_checkout",
-		"payment_success_url",
-		"payment_gateway_account",
-		"save_quotations_as_draft",
-	]
-
-	settings = frappe.get_doc("E Commerce Settings")
-
-	def map_into_e_commerce_settings(doctype, fields):
-		singles = frappe.qb.DocType("Singles")
-		query = (
-			frappe.qb.from_(singles)
-			.select(singles["field"], singles.value)
-			.where((singles.doctype == doctype) & (singles["field"].isin(fields)))
-		)
-		data = query.run(as_dict=True)
-
-		# {'enable_attribute_filters': '1', ...}
-		mapper = {row.field: row.value for row in data}
-
-		for key, value in mapper.items():
-			value = cint(value) if (value and value.isdigit()) else value
-			settings.update({key: value})
-
-		settings.save()
-
-	# shift data to E Commerce Settings
-	map_into_e_commerce_settings("Products Settings", products_settings_fields)
-	map_into_e_commerce_settings("Shopping Cart Settings", shopping_cart_settings_fields)
-
-	# move filters and attributes tables to E Commerce Settings from Products Settings
-	for doctype in ("Website Filter Field", "Website Attribute"):
-		frappe.db.set_value(
-			doctype,
-			{"parent": "Products Settings"},
-			{"parenttype": "E Commerce Settings", "parent": "E Commerce Settings"},
-			update_modified=False,
-		)
diff --git a/erpnext/patches/v13_0/shopping_cart_to_ecommerce.py b/erpnext/patches/v13_0/shopping_cart_to_ecommerce.py
deleted file mode 100644
index 35710a9..0000000
--- a/erpnext/patches/v13_0/shopping_cart_to_ecommerce.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import click
-import frappe
-
-
-def execute():
-
-	frappe.delete_doc("DocType", "Shopping Cart Settings", ignore_missing=True)
-	frappe.delete_doc("DocType", "Products Settings", ignore_missing=True)
-	frappe.delete_doc("DocType", "Supplier Item Group", ignore_missing=True)
-
-	if frappe.db.get_single_value("E Commerce Settings", "enabled"):
-		notify_users()
-
-
-def notify_users():
-
-	click.secho(
-		"Shopping cart and Product settings are merged into E-commerce settings.\n"
-		"Checkout the documentation to learn more:"
-		"https://docs.erpnext.com/docs/v13/user/manual/en/e_commerce/set_up_e_commerce",
-		fg="yellow",
-	)
-
-	note = frappe.new_doc("Note")
-	note.title = "New E-Commerce Module"
-	note.public = 1
-	note.notify_on_login = 1
-	note.content = """<div class="ql-editor read-mode"><p>You are seeing this message because Shopping Cart is enabled on your site. </p><p><br></p><p>Shopping Cart Settings and Products settings are now merged into "E Commerce Settings". </p><p><br></p><p>You can learn about new and improved E-Commerce features in the official documentation.</p><ol><li data-list="bullet"><span class="ql-ui" contenteditable="false"></span><a href="https://docs.erpnext.com/docs/v13/user/manual/en/e_commerce/set_up_e_commerce" rel="noopener noreferrer">https://docs.erpnext.com/docs/v13/user/manual/en/e_commerce/set_up_e_commerce</a></li></ol><p><br></p></div>"""
-	note.insert(ignore_mandatory=True)
diff --git a/erpnext/patches/v14_0/create_accounting_dimensions_in_sales_order_item.py b/erpnext/patches/v14_0/create_accounting_dimensions_in_sales_order_item.py
new file mode 100644
index 0000000..8f77c35
--- /dev/null
+++ b/erpnext/patches/v14_0/create_accounting_dimensions_in_sales_order_item.py
@@ -0,0 +1,7 @@
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+	create_accounting_dimensions_for_doctype,
+)
+
+
+def execute():
+	create_accounting_dimensions_for_doctype(doctype="Sales Order Item")
diff --git a/erpnext/patches/v14_0/migrate_delivery_stop_lock_field.py b/erpnext/patches/v14_0/migrate_delivery_stop_lock_field.py
new file mode 100644
index 0000000..c9ec1e1
--- /dev/null
+++ b/erpnext/patches/v14_0/migrate_delivery_stop_lock_field.py
@@ -0,0 +1,7 @@
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+
+def execute():
+	if frappe.db.has_column("Delivery Stop", "lock"):
+		rename_field("Delivery Stop", "lock", "locked")
diff --git a/erpnext/patches/v14_0/rename_over_order_allowance_field.py b/erpnext/patches/v14_0/rename_over_order_allowance_field.py
new file mode 100644
index 0000000..a81fe88
--- /dev/null
+++ b/erpnext/patches/v14_0/rename_over_order_allowance_field.py
@@ -0,0 +1,15 @@
+from frappe.model.utils.rename_field import rename_field
+
+
+def execute():
+	rename_field(
+		"Buying Settings",
+		"over_order_allowance",
+		"blanket_order_allowance",
+	)
+
+	rename_field(
+		"Selling Settings",
+		"over_order_allowance",
+		"blanket_order_allowance",
+	)
diff --git a/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py b/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py
index a53adf1..9a2a39f 100644
--- a/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py
+++ b/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py
@@ -11,6 +11,9 @@
 	asset_depreciation_schedules_map = get_asset_depreciation_schedules_map()
 
 	for asset in assets:
+		if not asset_depreciation_schedules_map.get(asset.name):
+			continue
+
 		depreciation_schedules = asset_depreciation_schedules_map[asset.name]
 
 		for fb_row in asset_finance_books_map[asset.name]:
diff --git a/erpnext/patches/v15_0/delete_ecommerce_doctypes.py b/erpnext/patches/v15_0/delete_ecommerce_doctypes.py
new file mode 100644
index 0000000..af03987
--- /dev/null
+++ b/erpnext/patches/v15_0/delete_ecommerce_doctypes.py
@@ -0,0 +1,30 @@
+import click
+import frappe
+
+
+def execute():
+	if "webshop" in frappe.get_installed_apps():
+		return
+
+	if not frappe.db.table_exists("Website Item"):
+		return
+
+	doctypes = [
+		"E Commerce Settings",
+		"Website Item",
+		"Recommended Items",
+		"Item Review",
+		"Wishlist Item",
+		"Wishlist",
+		"Website Offer",
+		"Website Item Tabbed Section",
+	]
+
+	for doctype in doctypes:
+		frappe.delete_doc("DocType", doctype, ignore_missing=True)
+
+	click.secho(
+		"ECommerce is renamed and moved to a separate app"
+		"Please install the app for ECommerce features: https://github.com/frappe/webshop",
+		fg="yellow",
+	)
diff --git a/erpnext/patches/v15_0/delete_payment_gateway_doctypes.py b/erpnext/patches/v15_0/delete_payment_gateway_doctypes.py
new file mode 100644
index 0000000..959b065
--- /dev/null
+++ b/erpnext/patches/v15_0/delete_payment_gateway_doctypes.py
@@ -0,0 +1,6 @@
+import frappe
+
+
+def execute():
+	for dt in ("GoCardless Settings", "GoCardless Mandate", "Mpesa Settings"):
+		frappe.delete_doc("DocType", dt, ignore_missing=True)
diff --git a/erpnext/patches/v15_0/update_sre_from_voucher_details.py b/erpnext/patches/v15_0/update_sre_from_voucher_details.py
new file mode 100644
index 0000000..06ba553
--- /dev/null
+++ b/erpnext/patches/v15_0/update_sre_from_voucher_details.py
@@ -0,0 +1,18 @@
+import frappe
+from frappe.query_builder.functions import IfNull
+
+
+def execute():
+	columns = frappe.db.get_table_columns("Stock Reservation Entry")
+
+	if set(["against_pick_list", "against_pick_list_item"]).issubset(set(columns)):
+		sre = frappe.qb.DocType("Stock Reservation Entry")
+		(
+			frappe.qb.update(sre)
+			.set(sre.from_voucher_type, "Pick List")
+			.set(sre.from_voucher_no, sre.against_pick_list)
+			.set(sre.from_voucher_detail_no, sre.against_pick_list_item)
+			.where(
+				(IfNull(sre.against_pick_list, "") != "") & (IfNull(sre.against_pick_list_item, "") != "")
+			)
+		).run()
diff --git a/erpnext/portal/doctype/homepage/homepage.js b/erpnext/portal/doctype/homepage/homepage.js
index 59f808a..6797904 100644
--- a/erpnext/portal/doctype/homepage/homepage.js
+++ b/erpnext/portal/doctype/homepage/homepage.js
@@ -19,12 +19,3 @@
 		});
 	},
 });
-
-frappe.ui.form.on('Homepage Featured Product', {
-	view: function(frm, cdt, cdn) {
-		var child= locals[cdt][cdn];
-		if (child.item_code && child.route) {
-			window.open('/' + child.route, '_blank');
-		}
-	}
-});
diff --git a/erpnext/portal/doctype/homepage/homepage.json b/erpnext/portal/doctype/homepage/homepage.json
index 73f816d..2b891f7 100644
--- a/erpnext/portal/doctype/homepage/homepage.json
+++ b/erpnext/portal/doctype/homepage/homepage.json
@@ -15,10 +15,7 @@
   "description",
   "hero_image",
   "slideshow",
-  "hero_section",
-  "products_section",
-  "products_url",
-  "products"
+  "hero_section"
  ],
  "fields": [
   {
@@ -86,30 +83,11 @@
    "fieldtype": "Link",
    "label": "Homepage Section",
    "options": "Homepage Section"
-  },
-  {
-   "fieldname": "products_section",
-   "fieldtype": "Section Break",
-   "label": "Products"
-  },
-  {
-   "default": "/all-products",
-   "fieldname": "products_url",
-   "fieldtype": "Data",
-   "label": "URL for \"All Products\""
-  },
-  {
-   "description": "Products to be shown on website homepage",
-   "fieldname": "products",
-   "fieldtype": "Table",
-   "label": "Products",
-   "options": "Homepage Featured Product",
-   "width": "40px"
   }
  ],
  "issingle": 1,
  "links": [],
- "modified": "2021-02-18 13:29:29.531639",
+ "modified": "2022-12-19 21:10:29.127277",
  "modified_by": "Administrator",
  "module": "Portal",
  "name": "Homepage",
@@ -138,6 +116,7 @@
  ],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "title_field": "company",
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/portal/doctype/homepage/homepage.py b/erpnext/portal/doctype/homepage/homepage.py
index 0d2e360..c0a0c07 100644
--- a/erpnext/portal/doctype/homepage/homepage.py
+++ b/erpnext/portal/doctype/homepage/homepage.py
@@ -12,26 +12,3 @@
 		if not self.description:
 			self.description = frappe._("This is an example website auto-generated from ERPNext")
 		delete_page_cache("home")
-
-	def setup_items(self):
-		for d in frappe.get_all(
-			"Website Item",
-			fields=["name", "item_name", "description", "website_image", "route"],
-			filters={"published": 1},
-			limit=3,
-		):
-
-			doc = frappe.get_doc("Website Item", d.name)
-			if not doc.route:
-				# set missing route
-				doc.save()
-			self.append(
-				"products",
-				dict(
-					item_code=d.name,
-					item_name=d.item_name,
-					description=d.description,
-					image=d.website_image,
-					route=d.route,
-				),
-			)
diff --git a/erpnext/portal/doctype/homepage_featured_product/__init__.py b/erpnext/portal/doctype/homepage_featured_product/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/portal/doctype/homepage_featured_product/__init__.py
+++ /dev/null
diff --git a/erpnext/portal/doctype/homepage_featured_product/homepage_featured_product.json b/erpnext/portal/doctype/homepage_featured_product/homepage_featured_product.json
deleted file mode 100644
index 63789e3..0000000
--- a/erpnext/portal/doctype/homepage_featured_product/homepage_featured_product.json
+++ /dev/null
@@ -1,118 +0,0 @@
-{
- "actions": [],
- "autoname": "hash",
- "creation": "2016-04-22 05:57:06.261401",
- "doctype": "DocType",
- "document_type": "Document",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "item_code",
-  "col_break1",
-  "item_name",
-  "view",
-  "section_break_5",
-  "description",
-  "column_break_7",
-  "image",
-  "thumbnail",
-  "route"
- ],
- "fields": [
-  {
-   "bold": 1,
-   "fieldname": "item_code",
-   "fieldtype": "Link",
-   "in_filter": 1,
-   "in_list_view": 1,
-   "label": "Item",
-   "oldfieldname": "item_code",
-   "oldfieldtype": "Link",
-   "options": "Website Item",
-   "print_width": "150px",
-   "reqd": 1,
-   "search_index": 1,
-   "width": "150px"
-  },
-  {
-   "fieldname": "col_break1",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fetch_from": "item_code.item_name",
-   "fetch_if_empty": 1,
-   "fieldname": "item_name",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Item Name",
-   "oldfieldname": "item_name",
-   "oldfieldtype": "Data",
-   "print_hide": 1,
-   "print_width": "150",
-   "read_only": 1,
-   "reqd": 1,
-   "width": "150"
-  },
-  {
-   "fieldname": "view",
-   "fieldtype": "Button",
-   "in_list_view": 1,
-   "label": "View"
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "section_break_5",
-   "fieldtype": "Section Break",
-   "label": "Details"
-  },
-  {
-   "fetch_from": "item_code.web_long_description",
-   "fieldname": "description",
-   "fieldtype": "Text Editor",
-   "in_filter": 1,
-   "in_list_view": 1,
-   "label": "Description",
-   "oldfieldname": "description",
-   "oldfieldtype": "Small Text",
-   "print_width": "300px",
-   "width": "300px"
-  },
-  {
-   "fieldname": "column_break_7",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fetch_from": "item_code.website_image",
-   "fetch_if_empty": 1,
-   "fieldname": "image",
-   "fieldtype": "Attach Image",
-   "label": "Image"
-  },
-  {
-   "fetch_from": "item_code.thumbnail",
-   "fieldname": "thumbnail",
-   "fieldtype": "Attach Image",
-   "hidden": 1,
-   "label": "Thumbnail"
-  },
-  {
-   "fetch_from": "item_code.route",
-   "fieldname": "route",
-   "fieldtype": "Small Text",
-   "label": "route",
-   "read_only": 1
-  }
- ],
- "index_web_pages_for_search": 1,
- "istable": 1,
- "links": [],
- "modified": "2021-02-18 13:05:50.669311",
- "modified_by": "Administrator",
- "module": "Portal",
- "name": "Homepage Featured Product",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "DESC"
-}
\ No newline at end of file
diff --git a/erpnext/portal/doctype/homepage_featured_product/homepage_featured_product.py b/erpnext/portal/doctype/homepage_featured_product/homepage_featured_product.py
deleted file mode 100644
index c21461d..0000000
--- a/erpnext/portal/doctype/homepage_featured_product/homepage_featured_product.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-from frappe.model.document import Document
-
-
-class HomepageFeaturedProduct(Document):
-	pass
diff --git a/erpnext/portal/utils.py b/erpnext/portal/utils.py
index c8b03e6..903d4a6 100644
--- a/erpnext/portal/utils.py
+++ b/erpnext/portal/utils.py
@@ -1,10 +1,4 @@
 import frappe
-from frappe.utils.nestedset import get_root_of
-
-from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import (
-	get_shopping_cart_settings,
-)
-from erpnext.e_commerce.shopping_cart.cart import get_debtors_account
 
 
 def set_default_role(doc, method):
@@ -56,26 +50,7 @@
 	party = frappe.new_doc(doctype)
 	fullname = frappe.utils.get_fullname(user)
 
-	if doctype == "Customer":
-		cart_settings = get_shopping_cart_settings()
-
-		if cart_settings.enable_checkout:
-			debtors_account = get_debtors_account(cart_settings)
-		else:
-			debtors_account = ""
-
-		party.update(
-			{
-				"customer_name": fullname,
-				"customer_type": "Individual",
-				"customer_group": cart_settings.default_customer_group,
-				"territory": get_root_of("Territory"),
-			}
-		)
-
-		if debtors_account:
-			party.update({"accounts": [{"company": cart_settings.company, "account": debtors_account}]})
-	else:
+	if not doctype == "Customer":
 		party.update(
 			{
 				"supplier_name": fullname,
diff --git a/erpnext/projects/doctype/task_depends_on/task_depends_on.json b/erpnext/projects/doctype/task_depends_on/task_depends_on.json
index 5102986..3300b7e 100644
--- a/erpnext/projects/doctype/task_depends_on/task_depends_on.json
+++ b/erpnext/projects/doctype/task_depends_on/task_depends_on.json
@@ -24,6 +24,7 @@
   },
   {
    "fetch_from": "task.subject",
+   "fetch_if_empty": 1,
    "fieldname": "subject",
    "fieldtype": "Text",
    "in_list_view": 1,
@@ -31,7 +32,6 @@
    "read_only": 1
   },
   {
-   "fetch_from": "task.project",
    "fieldname": "project",
    "fieldtype": "Text",
    "label": "Project",
@@ -40,7 +40,7 @@
  ],
  "istable": 1,
  "links": [],
- "modified": "2023-10-09 11:34:14.335853",
+ "modified": "2023-10-17 12:45:21.536165",
  "modified_by": "Administrator",
  "module": "Projects",
  "name": "Task Depends On",
diff --git a/erpnext/public/images/erpnext-favicon.svg b/erpnext/public/images/erpnext-favicon.svg
index 6bc6b2c..768e6e5 100644
--- a/erpnext/public/images/erpnext-favicon.svg
+++ b/erpnext/public/images/erpnext-favicon.svg
@@ -1,5 +1,5 @@
 <svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M0 12C0 5.37258 5.37258 0 12 0H88C94.6274 0 100 5.37258 100 12V88C100 94.6274 94.6274 100 88 100H12C5.37258 100 0 94.6274 0 88V12Z" fill="#0089FF"/>
+<path d="M0 12C0 5.37258 5.37258 0 12 0H88C94.6274 0 100 5.37258 100 12V88C100 94.6274 94.6274 100 88 100H12C5.37258 100 0 94.6274 0 88V12Z" fill="#171717"/>
 <path d="M65.7097 32.9462H67.3871V24H33V32.9462H43.9032H65.7097Z" fill="white"/>
 <path d="M43.9032 66.2151V53.914H65.7097V44.9677H43.9032H33V75.1613H67.6667V66.2151H43.9032Z" fill="white"/>
 </svg>
diff --git a/erpnext/public/images/erpnext-logo.png b/erpnext/public/images/erpnext-logo.png
index 3090727..b4c2749 100644
--- a/erpnext/public/images/erpnext-logo.png
+++ b/erpnext/public/images/erpnext-logo.png
Binary files differ
diff --git a/erpnext/public/images/erpnext-logo.svg b/erpnext/public/images/erpnext-logo.svg
index 6bc6b2c..768e6e5 100644
--- a/erpnext/public/images/erpnext-logo.svg
+++ b/erpnext/public/images/erpnext-logo.svg
@@ -1,5 +1,5 @@
 <svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M0 12C0 5.37258 5.37258 0 12 0H88C94.6274 0 100 5.37258 100 12V88C100 94.6274 94.6274 100 88 100H12C5.37258 100 0 94.6274 0 88V12Z" fill="#0089FF"/>
+<path d="M0 12C0 5.37258 5.37258 0 12 0H88C94.6274 0 100 5.37258 100 12V88C100 94.6274 94.6274 100 88 100H12C5.37258 100 0 94.6274 0 88V12Z" fill="#171717"/>
 <path d="M65.7097 32.9462H67.3871V24H33V32.9462H43.9032H65.7097Z" fill="white"/>
 <path d="M43.9032 66.2151V53.914H65.7097V44.9677H43.9032H33V75.1613H67.6667V66.2151H43.9032Z" fill="white"/>
 </svg>
diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js
index a2e4bda..7879173 100644
--- a/erpnext/public/js/controllers/accounts.js
+++ b/erpnext/public/js/controllers/accounts.js
@@ -30,7 +30,6 @@
 							filters: {
 								"account_type": account_type,
 								"company": doc.company,
-								"disabled": 0
 							}
 						}
 					});
@@ -116,7 +115,7 @@
 			account_head: function(frm, cdt, cdn) {
 				let d = locals[cdt][cdn];
 
-				if (doc.docstatus == 1) {
+				if (d.docstatus == 1) {
 					// Should not trigger any changes on change post submit
 					return;
 				}
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 70b70c3..6b613ce 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -193,7 +193,7 @@
 		frappe.flags.round_off_applicable_accounts = [];
 
 		if (me.frm.doc.company) {
-			return frappe.call({
+			frappe.call({
 				"method": "erpnext.controllers.taxes_and_totals.get_round_off_applicable_accounts",
 				"args": {
 					"company": me.frm.doc.company,
@@ -206,6 +206,11 @@
 				}
 			});
 		}
+
+		frappe.db.get_single_value("Accounts Settings", "round_row_wise_tax")
+			.then((round_row_wise_tax) => {
+				frappe.flags.round_row_wise_tax = round_row_wise_tax;
+			})
 	}
 
 	determine_exclusive_rate() {
@@ -346,6 +351,9 @@
 			$.each(me.frm.doc["taxes"] || [], function(i, tax) {
 				// tax_amount represents the amount of tax for the current step
 				var current_tax_amount = me.get_current_tax_amount(item, tax, item_tax_map);
+				if (frappe.flags.round_row_wise_tax) {
+					current_tax_amount = flt(current_tax_amount, precision("tax_amount", tax));
+				}
 
 				// Adjust divisional loss to the last item
 				if (tax.charge_type == "Actual") {
@@ -480,8 +488,15 @@
 		}
 
 		let item_wise_tax_amount = current_tax_amount * this.frm.doc.conversion_rate;
-		if (tax_detail && tax_detail[key])
-			item_wise_tax_amount += tax_detail[key][1];
+		if (frappe.flags.round_row_wise_tax) {
+			item_wise_tax_amount = flt(item_wise_tax_amount, precision("tax_amount", tax));
+			if (tax_detail && tax_detail[key]) {
+				item_wise_tax_amount += flt(tax_detail[key][1], precision("tax_amount", tax));
+			}
+		} else {
+			if (tax_detail && tax_detail[key])
+				item_wise_tax_amount += tax_detail[key][1];
+		}
 
 		tax_detail[key] = [tax_rate, flt(item_wise_tax_amount, precision("base_tax_amount", tax))];
 	}
diff --git a/erpnext/public/js/customer_reviews.js b/erpnext/public/js/customer_reviews.js
deleted file mode 100644
index e13ded6..0000000
--- a/erpnext/public/js/customer_reviews.js
+++ /dev/null
@@ -1,138 +0,0 @@
-$(() => {
-	class CustomerReviews {
-		constructor() {
-			this.bind_button_actions();
-			this.start = 0;
-			this.page_length = 10;
-		}
-
-		bind_button_actions() {
-			this.write_review();
-			this.view_more();
-		}
-
-		write_review() {
-			//TODO: make dialog popup on stray page
-			$('.page_content').on('click', '.btn-write-review', (e) => {
-				// Bind action on write a review button
-				const $btn = $(e.currentTarget);
-
-				let d = new frappe.ui.Dialog({
-					title: __("Write a Review"),
-					fields: [
-						{fieldname: "title", fieldtype: "Data", label: "Headline", reqd: 1},
-						{fieldname: "rating", fieldtype: "Rating", label: "Overall Rating", reqd: 1},
-						{fieldtype: "Section Break"},
-						{fieldname: "comment", fieldtype: "Small Text", label: "Your Review"}
-					],
-					primary_action: function() {
-						let data = d.get_values();
-						frappe.call({
-							method: "erpnext.e_commerce.doctype.item_review.item_review.add_item_review",
-							args: {
-								web_item: $btn.attr('data-web-item'),
-								title: data.title,
-								rating: data.rating,
-								comment: data.comment
-							},
-							freeze: true,
-							freeze_message: __("Submitting Review ..."),
-							callback: (r) => {
-								if (!r.exc) {
-									frappe.msgprint({
-										message: __("Thank you for submitting your review"),
-										title: __("Review Submitted"),
-										indicator: "green"
-									});
-									d.hide();
-									location.reload();
-								}
-							}
-						});
-					},
-					primary_action_label: __('Submit')
-				});
-				d.show();
-			});
-		}
-
-		view_more() {
-			$('.page_content').on('click', '.btn-view-more', (e) => {
-				// Bind action on view more button
-				const $btn = $(e.currentTarget);
-				$btn.prop('disabled', true);
-
-				this.start += this.page_length;
-				let me = this;
-
-				frappe.call({
-					method: "erpnext.e_commerce.doctype.item_review.item_review.get_item_reviews",
-					args: {
-						web_item: $btn.attr('data-web-item'),
-						start: me.start,
-						end: me.page_length
-					},
-					callback: (result) => {
-						if (result.message) {
-							let res = result.message;
-							me.get_user_review_html(res.reviews);
-
-							$btn.prop('disabled', false);
-							if (res.total_reviews <= (me.start + me.page_length)) {
-								$btn.hide();
-							}
-
-						}
-					}
-				});
-			});
-
-		}
-
-		get_user_review_html(reviews) {
-			let me = this;
-			let $content = $('.user-reviews');
-
-			reviews.forEach((review) => {
-				$content.append(`
-					<div class="mb-3 review">
-						<div class="d-flex">
-							<p class="mr-4 user-review-title">
-								<span>${__(review.review_title)}</span>
-							</p>
-							<div class="rating">
-								${me.get_review_stars(review.rating)}
-							</div>
-						</div>
-
-						<div class="product-description mb-4">
-							<p>
-								${__(review.comment)}
-							</p>
-						</div>
-						<div class="review-signature mb-2">
-							<span class="reviewer">${__(review.customer)}</span>
-							<span class="indicator grey" style="--text-on-gray: var(--gray-300);"></span>
-							<span class="reviewer">${__(review.published_on)}</span>
-						</div>
-					</div>
-				`);
-			});
-		}
-
-		get_review_stars(rating) {
-			let stars = ``;
-			for (let i = 1; i < 6; i++) {
-				let fill_class = i <= rating ? 'star-click' : '';
-				stars += `
-					<svg class="icon icon-sm ${fill_class}">
-						<use href="#icon-star"></use>
-					</svg>
-				`;
-			}
-			return stars;
-		}
-	}
-
-	new CustomerReviews();
-});
\ No newline at end of file
diff --git a/erpnext/public/js/erpnext-web.bundle.js b/erpnext/public/js/erpnext-web.bundle.js
index cbe899d..45c6a64 100644
--- a/erpnext/public/js/erpnext-web.bundle.js
+++ b/erpnext/public/js/erpnext-web.bundle.js
@@ -1,8 +1 @@
 import "./website_utils";
-import "./wishlist";
-import "./shopping_cart";
-import "./customer_reviews";
-import "../../e_commerce/product_ui/list";
-import "../../e_commerce/product_ui/views";
-import "../../e_commerce/product_ui/grid";
-import "../../e_commerce/product_ui/search";
\ No newline at end of file
diff --git a/erpnext/public/js/shopping_cart.js b/erpnext/public/js/shopping_cart.js
deleted file mode 100644
index d14740c..0000000
--- a/erpnext/public/js/shopping_cart.js
+++ /dev/null
@@ -1,243 +0,0 @@
-// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-// License: GNU General Public License v3. See license.txt
-
-// shopping cart
-frappe.provide("erpnext.e_commerce.shopping_cart");
-var shopping_cart = erpnext.e_commerce.shopping_cart;
-
-var getParams = function (url) {
-	var params = [];
-	var parser = document.createElement('a');
-	parser.href = url;
-	var query = parser.search.substring(1);
-	var vars = query.split('&');
-	for (var i = 0; i < vars.length; i++) {
-		var pair = vars[i].split('=');
-		params[pair[0]] = decodeURIComponent(pair[1]);
-	}
-	return params;
-};
-
-frappe.ready(function() {
-	var full_name = frappe.session && frappe.session.user_fullname;
-	// update user
-	if(full_name) {
-		$('.navbar li[data-label="User"] a')
-			.html('<i class="fa fa-fixed-width fa fa-user"></i> ' + full_name);
-	}
-	// set coupon code and sales partner code
-
-	var url_args = getParams(window.location.href);
-
-	var referral_coupon_code = url_args['cc'];
-	var referral_sales_partner = url_args['sp'];
-
-	var d = new Date();
-	// expires within 30 minutes
-	d.setTime(d.getTime() + (0.02 * 24 * 60 * 60 * 1000));
-	var expires = "expires="+d.toUTCString();
-	if (referral_coupon_code) {
-		document.cookie = "referral_coupon_code=" + referral_coupon_code + ";" + expires + ";path=/";
-	}
-	if (referral_sales_partner) {
-		document.cookie = "referral_sales_partner=" + referral_sales_partner + ";" + expires + ";path=/";
-	}
-	referral_coupon_code=frappe.get_cookie("referral_coupon_code");
-	referral_sales_partner=frappe.get_cookie("referral_sales_partner");
-
-	if (referral_coupon_code && $(".tot_quotation_discount").val()==undefined ) {
-		$(".txtcoupon").val(referral_coupon_code);
-	}
-	if (referral_sales_partner) {
-		$(".txtreferral_sales_partner").val(referral_sales_partner);
-	}
-
-	// update login
-	shopping_cart.show_shoppingcart_dropdown();
-	shopping_cart.set_cart_count();
-	shopping_cart.show_cart_navbar();
-});
-
-$.extend(shopping_cart, {
-	show_shoppingcart_dropdown: function() {
-		$(".shopping-cart").on('shown.bs.dropdown', function() {
-			if (!$('.shopping-cart-menu .cart-container').length) {
-				return frappe.call({
-					method: 'erpnext.e_commerce.shopping_cart.cart.get_shopping_cart_menu',
-					callback: function(r) {
-						if (r.message) {
-							$('.shopping-cart-menu').html(r.message);
-						}
-					}
-				});
-			}
-		});
-	},
-
-	update_cart: function(opts) {
-		if (frappe.session.user==="Guest") {
-			if (localStorage) {
-				localStorage.setItem("last_visited", window.location.pathname);
-			}
-			frappe.call('erpnext.e_commerce.api.get_guest_redirect_on_action').then((res) => {
-				window.location.href = res.message || "/login";
-			});
-		} else {
-			shopping_cart.freeze();
-			return frappe.call({
-				type: "POST",
-				method: "erpnext.e_commerce.shopping_cart.cart.update_cart",
-				args: {
-					item_code: opts.item_code,
-					qty: opts.qty,
-					additional_notes: opts.additional_notes !== undefined ? opts.additional_notes : undefined,
-					with_items: opts.with_items || 0
-				},
-				btn: opts.btn,
-				callback: function(r) {
-					shopping_cart.unfreeze();
-					shopping_cart.set_cart_count(true);
-					if(opts.callback)
-						opts.callback(r);
-				}
-			});
-		}
-	},
-
-	set_cart_count: function(animate=false) {
-		$(".intermediate-empty-cart").remove();
-
-		var cart_count = frappe.get_cookie("cart_count");
-		if(frappe.session.user==="Guest") {
-			cart_count = 0;
-		}
-
-		if(cart_count) {
-			$(".shopping-cart").toggleClass('hidden', false);
-		}
-
-		var $cart = $('.cart-icon');
-		var $badge = $cart.find("#cart-count");
-
-		if(parseInt(cart_count) === 0 || cart_count === undefined) {
-			$cart.css("display", "none");
-			$(".cart-tax-items").hide();
-			$(".btn-place-order").hide();
-			$(".cart-payment-addresses").hide();
-
-			let intermediate_empty_cart_msg = `
-				<div class="text-center w-100 intermediate-empty-cart mt-4 mb-4 text-muted">
-					${ __("Cart is Empty") }
-				</div>
-			`;
-			$(".cart-table").after(intermediate_empty_cart_msg);
-		}
-		else {
-			$cart.css("display", "inline");
-			$("#cart-count").text(cart_count);
-		}
-
-		if(cart_count) {
-			$badge.html(cart_count);
-
-			if (animate) {
-				$cart.addClass("cart-animate");
-				setTimeout(() => {
-					$cart.removeClass("cart-animate");
-				}, 500);
-			}
-		} else {
-			$badge.remove();
-		}
-	},
-
-	shopping_cart_update: function({item_code, qty, cart_dropdown, additional_notes}) {
-		shopping_cart.update_cart({
-			item_code,
-			qty,
-			additional_notes,
-			with_items: 1,
-			btn: this,
-			callback: function(r) {
-				if(!r.exc) {
-					$(".cart-items").html(r.message.items);
-					$(".cart-tax-items").html(r.message.total);
-					$(".payment-summary").html(r.message.taxes_and_totals);
-					shopping_cart.set_cart_count();
-
-					if (cart_dropdown != true) {
-						$(".cart-icon").hide();
-					}
-				}
-			},
-		});
-	},
-
-	show_cart_navbar: function () {
-		frappe.call({
-			method: "erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings.is_cart_enabled",
-			callback: function(r) {
-				$(".shopping-cart").toggleClass('hidden', r.message ? false : true);
-			}
-		});
-	},
-
-	toggle_button_class(button, remove, add) {
-		button.removeClass(remove);
-		button.addClass(add);
-	},
-
-	bind_add_to_cart_action() {
-		$('.page_content').on('click', '.btn-add-to-cart-list', (e) => {
-			const $btn = $(e.currentTarget);
-			$btn.prop('disabled', true);
-
-			if (frappe.session.user==="Guest") {
-				if (localStorage) {
-					localStorage.setItem("last_visited", window.location.pathname);
-				}
-				frappe.call('erpnext.e_commerce.api.get_guest_redirect_on_action').then((res) => {
-					window.location.href = res.message || "/login";
-				});
-				return;
-			}
-
-			$btn.addClass('hidden');
-			$btn.closest('.cart-action-container').addClass('d-flex');
-			$btn.parent().find('.go-to-cart').removeClass('hidden');
-			$btn.parent().find('.go-to-cart-grid').removeClass('hidden');
-			$btn.parent().find('.cart-indicator').removeClass('hidden');
-
-			const item_code = $btn.data('item-code');
-			erpnext.e_commerce.shopping_cart.update_cart({
-				item_code,
-				qty: 1
-			});
-
-		});
-	},
-
-	freeze() {
-		if (window.location.pathname !== "/cart") return;
-
-		if (!$('#freeze').length) {
-			let freeze = $('<div id="freeze" class="modal-backdrop fade"></div>')
-				.appendTo("body");
-
-			setTimeout(function() {
-				freeze.addClass("show");
-			}, 1);
-		} else {
-			$("#freeze").addClass("show");
-		}
-	},
-
-	unfreeze() {
-		if ($('#freeze').length) {
-			let freeze = $('#freeze').removeClass("show");
-			setTimeout(function() {
-				freeze.remove();
-			}, 1);
-		}
-	}
-});
diff --git a/erpnext/public/js/utils/item_selector.js b/erpnext/public/js/utils/item_selector.js
index 9fc2640..e74d291 100644
--- a/erpnext/public/js/utils/item_selector.js
+++ b/erpnext/public/js/utils/item_selector.js
@@ -97,14 +97,14 @@
 		}
 
 		var me = this;
-		frappe.link_search("Item", args, function(r) {
-			$.each(r.values, function(i, d) {
+		frappe.link_search("Item", args, function(results) {
+			$.each(results, function(i, d) {
 				if(!d.image) {
 					d.abbr = frappe.get_abbr(d.item_name);
 					d.color = frappe.get_palette(d.item_name);
 				}
 			});
-			me.dialog.results.html(frappe.render_template('item_selector', {'data':r.values}));
+			me.dialog.results.html(frappe.render_template('item_selector', {'data': results}));
 		});
 	}
 };
diff --git a/erpnext/public/js/wishlist.js b/erpnext/public/js/wishlist.js
deleted file mode 100644
index f6599e9..0000000
--- a/erpnext/public/js/wishlist.js
+++ /dev/null
@@ -1,204 +0,0 @@
-frappe.provide("erpnext.e_commerce.wishlist");
-var wishlist = erpnext.e_commerce.wishlist;
-
-frappe.provide("erpnext.e_commerce.shopping_cart");
-var shopping_cart = erpnext.e_commerce.shopping_cart;
-
-$.extend(wishlist, {
-	set_wishlist_count: function(animate=false) {
-		// set badge count for wishlist icon
-		var wish_count = frappe.get_cookie("wish_count");
-		if (frappe.session.user==="Guest") {
-			wish_count = 0;
-		}
-
-		if (wish_count) {
-			$(".wishlist").toggleClass('hidden', false);
-		}
-
-		var $wishlist = $('.wishlist-icon');
-		var $badge = $wishlist.find("#wish-count");
-
-		if (parseInt(wish_count) === 0 || wish_count === undefined) {
-			$wishlist.css("display", "none");
-		} else {
-			$wishlist.css("display", "inline");
-		}
-		if (wish_count) {
-			$badge.html(wish_count);
-			if (animate) {
-				$wishlist.addClass('cart-animate');
-				setTimeout(() => {
-					$wishlist.removeClass('cart-animate');
-				}, 500);
-			}
-		} else {
-			$badge.remove();
-		}
-	},
-
-	bind_move_to_cart_action: function() {
-		// move item to cart from wishlist
-		$('.page_content').on("click", ".btn-add-to-cart", (e) => {
-			const $move_to_cart_btn = $(e.currentTarget);
-			let item_code = $move_to_cart_btn.data("item-code");
-
-			shopping_cart.shopping_cart_update({
-				item_code,
-				qty: 1,
-				cart_dropdown: true
-			});
-
-			let success_action = function() {
-				const $card_wrapper = $move_to_cart_btn.closest(".wishlist-card");
-				$card_wrapper.addClass("wish-removed");
-			};
-			let args = { item_code: item_code };
-			this.add_remove_from_wishlist("remove", args, success_action, null, true);
-		});
-	},
-
-	bind_remove_action: function() {
-		// remove item from wishlist
-		let me = this;
-
-		$('.page_content').on("click", ".remove-wish", (e) => {
-			const $remove_wish_btn = $(e.currentTarget);
-			let item_code = $remove_wish_btn.data("item-code");
-
-			let success_action = function() {
-				const $card_wrapper = $remove_wish_btn.closest(".wishlist-card");
-				$card_wrapper.addClass("wish-removed");
-				if (frappe.get_cookie("wish_count") == 0) {
-					$(".page_content").empty();
-					me.render_empty_state();
-				}
-			};
-			let args = { item_code: item_code };
-			this.add_remove_from_wishlist("remove", args, success_action);
-		});
-	},
-
-	bind_wishlist_action() {
-		// 'wish'('like') or 'unwish' item in product listing
-		$('.page_content').on('click', '.like-action, .like-action-list', (e) => {
-			const $btn = $(e.currentTarget);
-			this.wishlist_action($btn);
-		});
-	},
-
-	wishlist_action(btn) {
-		const $wish_icon = btn.find('.wish-icon');
-		let me = this;
-
-		if (frappe.session.user==="Guest") {
-			if (localStorage) {
-				localStorage.setItem("last_visited", window.location.pathname);
-			}
-			this.redirect_guest();
-			return;
-		}
-
-		let success_action = function() {
-			erpnext.e_commerce.wishlist.set_wishlist_count(true);
-		};
-
-		if ($wish_icon.hasClass('wished')) {
-			// un-wish item
-			btn.removeClass("like-animate");
-			btn.addClass("like-action-wished");
-			this.toggle_button_class($wish_icon, 'wished', 'not-wished');
-
-			let args = { item_code: btn.data('item-code') };
-			let failure_action = function() {
-				me.toggle_button_class($wish_icon, 'not-wished', 'wished');
-			};
-			this.add_remove_from_wishlist("remove", args, success_action, failure_action);
-		} else {
-			// wish item
-			btn.addClass("like-animate");
-			btn.addClass("like-action-wished");
-			this.toggle_button_class($wish_icon, 'not-wished', 'wished');
-
-			let args = {item_code: btn.data('item-code')};
-			let failure_action = function() {
-				me.toggle_button_class($wish_icon, 'wished', 'not-wished');
-			};
-			this.add_remove_from_wishlist("add", args, success_action, failure_action);
-		}
-	},
-
-	toggle_button_class(button, remove, add) {
-		button.removeClass(remove);
-		button.addClass(add);
-	},
-
-	add_remove_from_wishlist(action, args, success_action, failure_action, async=false) {
-		/*	AJAX call to add or remove Item from Wishlist
-			action: "add" or "remove"
-			args: args for method (item_code, price, formatted_price),
-			success_action: method to execute on successs,
-			failure_action: method to execute on failure,
-			async: make call asynchronously (true/false).	*/
-		if (frappe.session.user==="Guest") {
-			if (localStorage) {
-				localStorage.setItem("last_visited", window.location.pathname);
-			}
-			this.redirect_guest();
-		} else {
-			let method = "erpnext.e_commerce.doctype.wishlist.wishlist.add_to_wishlist";
-			if (action === "remove") {
-				method = "erpnext.e_commerce.doctype.wishlist.wishlist.remove_from_wishlist";
-			}
-
-			frappe.call({
-				async: async,
-				type: "POST",
-				method: method,
-				args: args,
-				callback: function (r) {
-					if (r.exc) {
-						if (failure_action && (typeof failure_action === 'function')) {
-							failure_action();
-						}
-						frappe.msgprint({
-							message: __("Sorry, something went wrong. Please refresh."),
-							indicator: "red", title: __("Note")
-						});
-					} else if (success_action && (typeof success_action === 'function')) {
-						success_action();
-					}
-				}
-			});
-		}
-	},
-
-	redirect_guest() {
-		frappe.call('erpnext.e_commerce.api.get_guest_redirect_on_action').then((res) => {
-			window.location.href = res.message || "/login";
-		});
-	},
-
-	render_empty_state() {
-		$(".page_content").append(`
-			<div class="cart-empty frappe-card">
-				<div class="cart-empty-state">
-					<img src="/assets/erpnext/images/ui-states/cart-empty-state.png" alt="Empty Cart">
-				</div>
-				<div class="cart-empty-message mt-4">${ __('Wishlist is empty !') }</p>
-			</div>
-		`);
-	}
-
-});
-
-frappe.ready(function() {
-	if (window.location.pathname !== "/wishlist") {
-		$(".wishlist").toggleClass('hidden', true);
-		wishlist.set_wishlist_count();
-	} else {
-		wishlist.bind_move_to_cart_action();
-		wishlist.bind_remove_action();
-	}
-
-});
\ No newline at end of file
diff --git a/erpnext/public/scss/erpnext-web.bundle.scss b/erpnext/public/scss/erpnext-web.bundle.scss
index 6ef1892..18d7c6c 100644
--- a/erpnext/public/scss/erpnext-web.bundle.scss
+++ b/erpnext/public/scss/erpnext-web.bundle.scss
@@ -1,2 +1 @@
-@import "./shopping_cart";
 @import "./website";
diff --git a/erpnext/public/scss/shopping_cart.scss b/erpnext/public/scss/shopping_cart.scss
deleted file mode 100644
index 6ae464d..0000000
--- a/erpnext/public/scss/shopping_cart.scss
+++ /dev/null
@@ -1,1381 +0,0 @@
-@import "frappe/public/scss/common/mixins";
-
-:root {
-	--green-info: #38A160;
-	--product-bg-color: white;
-	--body-bg-color:  var(--gray-50);
-}
-
-body.product-page {
-	background: var(--body-bg-color);
-}
-
-.item-breadcrumbs {
-	.breadcrumb-container {
-		a {
-			color: var(--gray-900);
-		}
-	}
-}
-
-.carousel-control {
-	height: 42px;
-	width: 42px;
-	display: flex;
-	align-items: center;
-	justify-content: center;
-	background: white;
-	box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.08), 0px 1px 2px 1px rgba(0, 0, 0, 0.06);
-	border-radius: 100px;
-}
-
-.carousel-control-prev,
-.carousel-control-next {
-	opacity: 1;
-	width: 8%;
-
-	@media (max-width: 1200px) {
-		width: 10%;
-	}
-	@media (max-width: 768px) {
-		width: 15%;
-	}
-}
-
-.carousel-body {
-	position: absolute;
-	top: 0;
-	left: 0;
-	right: 0;
-	bottom: 0;
-}
-
-.carousel-content {
-	max-width: 400px;
-	margin-left: 5rem;
-	margin-right: 5rem;
-}
-
-.card {
-	border: none;
-}
-
-.product-category-section {
-	.card:hover {
-		box-shadow: 0px 16px 45px 6px rgba(0, 0, 0, 0.08), 0px 8px 10px -10px rgba(0, 0, 0, 0.04);
-	}
-
-	.card-grid {
-		display: grid;
-		grid-gap: 15px;
-		grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
-	}
-}
-
-.no-image-item {
-	height: 340px;
-	width: 340px;
-	background: var(--gray-100);
-	border-radius: var(--border-radius);
-	font-size: 2rem;
-	color: var(--gray-500);
-	display: flex;
-	align-items: center;
-	justify-content: center;
-}
-
-.item-card-group-section {
-	.card {
-		height: 100%;
-		align-items: center;
-		justify-content: center;
-
-		&:hover {
-			box-shadow: 0px 16px 60px rgba(0, 0, 0, 0.08), 0px 8px 30px -20px rgba(0, 0, 0, 0.04);
-			transition: box-shadow 400ms;
-		}
-	}
-
-	.card:hover, .card:focus-within {
-		.btn-add-to-cart-list {
-			visibility: visible;
-		}
-		.like-action {
-			visibility: visible;
-		}
-		.btn-explore-variants {
-			visibility: visible;
-		}
-	}
-
-
-	.card-img-container {
-		height: 210px;
-		width: 100%;
-	}
-
-	.card-img {
-		max-height: 210px;
-		object-fit: contain;
-		margin-top: 1.25rem;
-	}
-
-	.no-image {
-		@include flex(flex, center, center, null);
-		height: 220px;
-		background: var(--gray-100);
-		width: 100%;
-		border-radius: var(--border-radius) var(--border-radius) 0 0;
-		font-size: 2rem;
-		color: var(--gray-500);
-	}
-
-	.no-image-list {
-		@include flex(flex, center, center, null);
-		height: 150px;
-		background: var(--gray-100);
-		border-radius: var(--border-radius);
-		font-size: 2rem;
-		color: var(--gray-500);
-		margin-top: 15px;
-		margin-bottom: 15px;
-	}
-
-	.card-body-flex {
-		display: flex;
-		flex-direction: column;
-	}
-
-	.product-title {
-		font-size: 14px;
-		color: var(--gray-800);
-		font-weight: 500;
-	}
-
-	.product-description {
-		font-size: 12px;
-		color: var(--text-color);
-		margin: 20px 0;
-		display: -webkit-box;
-		-webkit-line-clamp: 6;
-		-webkit-box-orient: vertical;
-
-		p {
-			margin-bottom: 0.5rem;
-		}
-	}
-
-	.product-category {
-		font-size: 13px;
-		color: var(--text-muted);
-		margin: var(--margin-sm) 0;
-	}
-
-	.product-price {
-		font-size: 18px;
-		font-weight: 600;
-		color: var(--text-color);
-		margin: var(--margin-sm) 0;
-		margin-bottom: auto !important;
-
-		.striked-price {
-			font-weight: 500;
-			font-size: 15px;
-			color: var(--gray-500);
-		}
-	}
-
-	.product-info-green {
-		color: var(--green-info);
-		font-weight: 600;
-	}
-
-	.item-card {
-		padding: var(--padding-sm);
-		min-width: 300px;
-	}
-
-	.wishlist-card {
-		padding: var(--padding-sm);
-		min-width: 260px;
-		.card-body-flex {
-			display: flex;
-			flex-direction: column;
-		}
-	}
-}
-
-#products-list-area, #products-grid-area {
-	padding: 0 5px;
-}
-
-.list-row {
-	background-color: white;
-	padding-bottom: 1rem;
-	padding-top: 1.5rem !important;
-	border-radius: 8px;
-	border-bottom: 1px solid var(--gray-50);
-
-	&:hover, &:focus-within {
-		box-shadow: 0px 16px 60px rgba(0, 0, 0, 0.08), 0px 8px 30px -20px rgba(0, 0, 0, 0.04);
-		transition: box-shadow 400ms;
-
-		.btn-add-to-cart-list {
-			visibility: visible;
-		}
-		.like-action-list {
-			visibility: visible;
-		}
-		.btn-explore-variants {
-			visibility: visible;
-		}
-	}
-
-	.product-code {
-		padding-top: 0 !important;
-	}
-
-	.btn-explore-variants {
-		min-width: 135px;
-		max-height: 30px;
-		float: right;
-		padding: 0.25rem 1rem;
-	}
-}
-
-[data-doctype="Item Group"],
-#page-index {
-	.page-header {
-		font-size: 20px;
-		font-weight: 700;
-		color: var(--text-color);
-	}
-
-	.filters-section {
-		.title-section {
-			border-bottom: 1px solid var(--table-border-color);
-		}
-
-		.filter-title {
-			font-weight: 500;
-		}
-
-		.clear-filters {
-			font-size: 13px;
-		}
-
-		.filter-lookup-input {
-			background-color: white;
-			border: 1px solid var(--gray-300);
-
-			&:focus {
-				border: 1px solid var(--primary);
-			}
-		}
-
-		.filter-label {
-			font-size: 11px;
-			font-weight: 600;
-			color: var(--gray-700);
-			text-transform: uppercase;
-		}
-
-		.filter-block {
-			border-bottom: 1px solid var(--table-border-color);
-		}
-
-		.checkbox {
-			.label-area {
-				font-size: 13px;
-				color: var(--gray-800);
-			}
-		}
-	}
-}
-
-.product-filter {
-	width: 14px !important;
-	height: 14px !important;
-}
-
-.discount-filter {
-	&:before {
-		width: 14px !important;
-		height: 14px !important;
-	}
-}
-
-.list-image {
-	border: none !important;
-	overflow: hidden;
-	max-height: 200px;
-	background-color: white;
-}
-
-.product-container {
-	@include card($padding: var(--padding-md));
-	background-color: var(--product-bg-color) !important;
-	min-height: fit-content;
-
-	.product-details {
-		max-width: 50%;
-
-		.btn-add-to-cart {
-			font-size: 14px;
-		}
-	}
-
-	&.item-main {
-		.product-image {
-			width: 100%;
-		}
-	}
-
-	.expand {
-		max-width: 100% !important; // expand in absence of slideshow
-	}
-
-	@media (max-width: 789px) {
-		.product-details {
-			max-width: 90% !important;
-
-			.btn-add-to-cart {
-				font-size: 14px;
-			}
-		}
-	}
-
-	.btn-add-to-wishlist {
-		svg use {
-			--icon-stroke: #F47A7A;
-		}
-	}
-
-	.btn-view-in-wishlist {
-		svg use {
-			fill: #F47A7A;
-			--icon-stroke: none;
-		}
-	}
-
-	.product-title {
-		font-size: 16px;
-		font-weight: 600;
-		color: var(--text-color);
-		padding: 0 !important;
-	}
-
-	.product-description {
-		font-size: 13px;
-		color: var(--gray-800);
-	}
-
-	.product-image {
-		border-color: var(--table-border-color) !important;
-		padding: 15px;
-
-		@media (max-width: var(--md-width)) {
-			height: 300px;
-			width: 300px;
-		}
-
-		@media (min-width: var(--lg-width)) {
-			height: 350px;
-			width: 350px;
-		}
-
-	 	img {
-			object-fit: contain;
-		}
-	}
-
-	.item-slideshow {
-
-		@media (max-width: var(--md-width)) {
-			max-height: 320px;
-		}
-
-		@media (min-width: var(--lg-width)) {
-			max-height: 430px;
-		}
-
-		overflow: auto;
-	}
-
-	.item-slideshow-image {
-		height: 4rem;
-		width: 6rem;
-		object-fit: contain;
-		padding: 0.5rem;
-		border: 1px solid var(--table-border-color);
-		border-radius: 4px;
-		cursor: pointer;
-
-		&:hover, &.active {
-			border-color: var(--primary);
-		}
-	}
-
-	.item-cart {
-		.product-price {
-			font-size: 22px;
-			color: var(--text-color);
-			font-weight: 600;
-
-			.formatted-price {
-				color: var(--text-muted);
-				font-size: 14px;
-			}
-		}
-
-		.no-stock {
-			font-size: var(--text-base);
-		}
-
-		.offers-heading {
-			font-size: 16px !important;
-			color: var(--text-color);
-			.tag-icon {
-				--icon-stroke: var(--gray-500);
-			}
-		}
-
-		.w-30-40 {
-			width: 30%;
-
-			@media (max-width: 992px) {
-				width: 40%;
-			}
-		}
-	}
-
-	.tab-content {
-		font-size: 14px;
-	}
-}
-
-// Item Recommendations
-.recommended-item-section {
-	padding-right: 0;
-
-	.recommendation-header {
-		font-size: 16px;
-		font-weight: 500
-	}
-
-	.recommendation-container {
-		padding: .5rem;
-		min-height: 0px;
-
-		.r-item-image {
-			min-height: 100px;
-			width: 40%;
-
-			.r-product-image {
-				padding: 2px 15px;
-			}
-
-			.no-image-r-item {
-				display: flex; justify-content: center;
-				background-color: var(--gray-200);
-				align-items: center;
-				color: var(--gray-400);
-				margin-top: .15rem;
-				border-radius: 6px;
-				height: 100%;
-				font-size: 24px;
-			}
-		}
-
-		.r-item-info {
-			font-size: 14px;
-			padding-right: 0;
-			padding-left: 10px;
-			width: 60%;
-
-			a {
-				color: var(--gray-800);
-				font-weight: 400;
-			}
-
-			.item-price {
-				font-size: 15px;
-				font-weight: 600;
-				color: var(--text-color);
-			}
-
-			.striked-item-price {
-				font-weight: 500;
-				color: var(--gray-500);
-			}
-		}
-	}
-}
-
-.product-code {
-	padding: .5rem 0;
-	color: var(--text-muted);
-	font-size: 14px;
-	.product-item-group {
-		padding-right: .25rem;
-		border-right: solid 1px var(--text-muted);
-	}
-
-	.product-item-code {
-		padding-left: .5rem;
-	}
-}
-
-.item-configurator-dialog {
-	.modal-body {
-		padding-bottom: var(--padding-xl);
-
-		.status-area {
-			.alert {
-				padding: var(--padding-xs) var(--padding-sm);
-				font-size: var(--text-sm);
-			}
-		}
-
-		.form-layout {
-			max-height: 50vh;
-			overflow-y: auto;
-		}
-
-		.section-body {
-			.form-column {
-				.form-group {
-					.control-label {
-						font-size: var(--text-md);
-						color: var(--gray-700);
-					}
-
-					.help-box {
-						margin-top: 2px;
-						font-size: var(--text-sm);
-					}
-				}
-			}
-		}
-	}
-}
-
-.item-group-slideshow {
-
-	.carousel-inner.rounded-carousel {
-		border-radius: var(--card-border-radius);
-	}
-}
-
-.sub-category-container {
-	padding-bottom: .5rem;
-	margin-bottom: 1.25rem;
-	border-bottom: 1px solid var(--table-border-color);
-
-	.heading {
-		color: var(--gray-500);
-	}
-}
-
-.scroll-categories {
-	.category-pill {
-		display: inline-block;
-		width: fit-content;
-		padding: 6px 12px;
-		margin-bottom: 8px;
-		background-color: #ecf5fe;
-		font-size: 14px;
-		border-radius: 18px;
-		color: var(--blue-500);
-	}
-}
-
-
-.shopping-badge {
-	position: relative;
-	top: -10px;
-	left: -12px;
-	background: var(--red-600);
-	align-items: center;
-	height: 16px;
-	font-size: 10px;
-	border-radius: 50%;
-}
-
-
-.cart-animate {
-	animation: wiggle 0.5s linear;
-}
-@keyframes wiggle {
-	8%,
-	41% {
-		transform: translateX(-10px);
-	}
-	25%,
-	58% {
-		transform: translate(10px);
-	}
-	75% {
-		transform: translate(-5px);
-	}
-	92% {
-		transform: translate(5px);
-	}
-	0%,
-	100% {
-		transform: translate(0);
-	}
-}
-
-.total-discount {
-	font-size: 14px;
-	color: var(--primary-color) !important;
-}
-
-#page-cart {
-	.shopping-cart-header {
-		font-weight: bold;
-	}
-
-	.cart-container {
-		color: var(--text-color);
-
-		.frappe-card {
-			display: flex;
-			flex-direction: column;
-			justify-content: space-between;
-			height: fit-content;
-		}
-
-		.cart-items-header {
-			font-weight: 600;
-		}
-
-		.cart-table {
-			tr {
-				margin-bottom: 1rem;
-			}
-
-			th, tr, td {
-				border-color: var(--border-color);
-				border-width: 1px;
-			}
-
-			th {
-				font-weight: normal;
-				font-size: 13px;
-				color: var(--text-muted);
-				padding: var(--padding-sm) 0;
-			}
-
-			td {
-				padding: var(--padding-sm) 0;
-				color: var(--text-color);
-			}
-
-			.cart-item-image {
-				width: 20%;
-				min-width: 100px;
-				img {
-					max-height: 112px;
-				}
-			}
-
-			.cart-items {
-				.item-title {
-					width: 80%;
-					font-size: 14px;
-					font-weight: 500;
-					color: var(--text-color);
-				}
-
-				.item-subtitle {
-					color: var(--text-muted);
-					font-size: 13px;
-				}
-
-				.item-subtotal {
-					font-size: 14px;
-					font-weight: 500;
-				}
-
-				.sm-item-subtotal {
-					font-size: 14px;
-					font-weight: 500;
-					display: none;
-
-					@media (max-width: 992px) {
-						display: unset !important;
-					}
-				}
-
-				.item-rate {
-					font-size: 13px;
-					color: var(--text-muted);
-				}
-
-				.free-tag {
-					padding: 4px 8px;
-					border-radius: 4px;
-					background-color: var(--dark-green-50);
-				}
-
-				textarea {
-					width: 80%;
-					height: 60px;
-					font-size: 14px;
-				}
-
-			}
-
-			.cart-tax-items {
-				.item-grand-total {
-					font-size: 16px;
-					font-weight: 700;
-					color: var(--text-color);
-				}
-			}
-
-			.column-sm-view {
-				@media (max-width: 992px) {
-					display: none !important;
-				}
-			}
-
-			.item-column {
-				width: 50%;
-				@media (max-width: 992px) {
-					width: 70%;
-				}
-			}
-
-			.remove-cart-item {
-				border-radius: 6px;
-				border: 1px solid var(--gray-100);
-				width: 28px;
-				height: 28px;
-				font-weight: 300;
-				color: var(--gray-700);
-				background-color: var(--gray-100);
-				float: right;
-				cursor: pointer;
-				margin-top: .25rem;
-				justify-content: center;
-			}
-
-			.remove-cart-item-logo {
-				margin-top: 2px;
-				margin-left: 2.2px;
-				fill: var(--gray-700) !important;
-			}
-		}
-
-		.cart-payment-addresses {
-			hr {
-				border-color: var(--border-color);
-			}
-		}
-
-		.payment-summary {
-			h6 {
-				padding-bottom: 1rem;
-				border-bottom: solid 1px var(--gray-200);
-			}
-
-			table {
-				font-size: 14px;
-				td {
-					padding: 0;
-					padding-top: 0.35rem !important;
-					border: none !important;
-				}
-
-				&.grand-total {
-					border-top: solid 1px var(--gray-200);
-				}
-			}
-
-			.bill-label {
-				color: var(--gray-600);
-			}
-
-			.bill-content {
-				font-weight: 500;
-				&.net-total {
-					font-size: 16px;
-					font-weight: 600;
-				}
-			}
-
-			.btn-coupon-code {
-				font-size: 14px;
-				border: dashed 1px var(--gray-400);
-				box-shadow: none;
-			}
-		}
-
-		.number-spinner {
-			width: 75%;
-			min-width: 105px;
-			.cart-btn {
-				border: none;
-				background: var(--gray-100);
-				box-shadow: none;
-				width: 24px;
-				height: 28px;
-				align-items: center;
-				justify-content: center;
-				display: flex;
-				font-size: 20px;
-				font-weight: 300;
-				color: var(--gray-700);
-			}
-
-			.cart-qty {
-				height: 28px;
-				font-size: 13px;
-				&:disabled {
-					background: var(--gray-100);
-					opacity: 0.65;
-				}
-			}
-		}
-
-		.place-order-container {
-			.btn-place-order {
-				float: right;
-			}
-		}
-	}
-
-	.t-and-c-container {
-		padding: 1.5rem;
-	}
-
-	.t-and-c-terms {
-		font-size: 14px;
-	}
-}
-
-.no-image-cart-item {
-	max-height: 112px;
-	display: flex; justify-content: center;
-	background-color: var(--gray-200);
-	align-items: center;
-	color: var(--gray-400);
-	margin-top: .15rem;
-	border-radius: 6px;
-	height: 100%;
-	font-size: 24px;
-}
-
-.cart-empty.frappe-card {
-	min-height: 76vh;
-	@include flex(flex, center, center, column);
-
-	.cart-empty-message {
-		font-size: 18px;
-		color: var(--text-color);
-		font-weight: bold;
-	}
-}
-
-.address-card {
-	.card-title {
-		font-size: 14px;
-		font-weight: 500;
-	}
-
-	.card-body {
-		max-width: 80%;
-	}
-
-	.card-text {
-		font-size: 13px;
-		color: var(--gray-700);
-	}
-
-	.card-link {
-		font-size: 13px;
-
-		svg use {
-			stroke: var(--primary-color);
-		}
-	}
-
-	.btn-change-address {
-		border: 1px solid var(--primary-color);
-		color: var(--primary-color);
-		box-shadow: none;
-	}
-}
-
-.address-header {
-	margin-top: .15rem;padding: 0;
-}
-
-.btn-new-address {
-	float: right;
-	font-size: 15px !important;
-	color: var(--primary-color) !important;
-}
-
-.btn-new-address:hover, .btn-change-address:hover {
-	color: var(--primary-color) !important;
-}
-
-.modal .address-card {
-	.card-body {
-		padding: var(--padding-sm);
-		border-radius: var(--border-radius);
-		border: 1px solid var(--dark-border-color);
-	}
-}
-
-.cart-indicator {
-	position: absolute;
-	text-align: center;
-	width: 22px;
-	height: 22px;
-	left: calc(100% - 40px);
-	top: 22px;
-
-	border-radius: 66px;
-	box-shadow: 0px 2px 6px rgba(17, 43, 66, 0.08), 0px 1px 4px rgba(17, 43, 66, 0.1);
-	background: white;
-	color: var(--primary-color);
-	font-size: 14px;
-
-	&.list-indicator {
-		position: unset;
-		margin-left: auto;
-	}
-}
-
-
-.like-action {
-	visibility: hidden;
-	text-align: center;
-	position: absolute;
-	cursor: pointer;
-	width: 28px;
-	height: 28px;
-	left: 20px;
-	top: 20px;
-
-	/* White */
-	background: white;
-	box-shadow: 0px 2px 6px rgba(17, 43, 66, 0.08), 0px 1px 4px rgba(17, 43, 66, 0.1);
-	border-radius: 66px;
-
-	&.like-action-wished {
-		visibility: visible !important;
-	}
-
-	@media (max-width: 992px) {
-		visibility: visible !important;
-	}
-}
-
-.like-action-list {
-	visibility: hidden;
-	text-align: center;
-	position: absolute;
-	cursor: pointer;
-	width: 28px;
-	height: 28px;
-	left: 20px;
-	top: 0;
-
-	/* White */
-	background: white;
-	box-shadow: 0px 2px 6px rgba(17, 43, 66, 0.08), 0px 1px 4px rgba(17, 43, 66, 0.1);
-	border-radius: 66px;
-
-	&.like-action-wished {
-		visibility: visible !important;
-	}
-
-	@media (max-width: 992px) {
-		visibility: visible !important;
-	}
-}
-
-.like-action-item-fp {
-	visibility: visible !important;
-	position: unset;
-	float: right;
-}
-
-.like-animate {
-	animation: expand cubic-bezier(0.04, 0.4, 0.5, 0.95) 1.6s forwards 1;
-}
-
-@keyframes expand {
-	30% {
-	  transform: scale(1.3);
-	}
-	50% {
-	  transform: scale(0.8);
-	}
-	70% {
-		transform: scale(1.1);
-	}
-	100% {
-	  transform: scale(1);
-	}
-  }
-
-.not-wished {
-	cursor: pointer;
-	--icon-stroke: #F47A7A !important;
-
-	&:hover {
-		fill: #F47A7A;
-	}
-}
-
-.wished {
-	--icon-stroke: none;
-	fill: #F47A7A !important;
-}
-
-.list-row-checkbox {
-	&:before {
-		display: none;
-	}
-
-	&:checked:before {
-		display: block;
-		z-index: 1;
-	}
-}
-
-#pay-for-order {
-	padding: .5rem 1rem; // Pay button in SO
-}
-
-.btn-explore-variants {
-	visibility: hidden;
-	box-shadow: none;
-	margin: var(--margin-sm) 0;
-	width: 90px;
-	max-height: 50px; // to avoid resizing on window resize
-	flex: none;
-	transition: 0.3s ease;
-
-	color: white;
-	background-color: var(--orange-500);
-	border: 1px solid var(--orange-500);
-	font-size: 13px;
-
-	&:hover {
-		color: white;
-	}
-}
-
-.btn-add-to-cart-list{
-	visibility: hidden;
-	box-shadow: none;
-	margin: var(--margin-sm) 0;
-	// margin-top: auto !important;
-	max-height: 50px; // to avoid resizing on window resize
-	flex: none;
-	transition: 0.3s ease;
-
-	font-size: 13px;
-
-	&:hover {
-		color: white;
-	}
-
-	@media (max-width: 992px) {
-		visibility: visible !important;
-	}
-}
-
-.go-to-cart-grid {
-	max-height: 30px;
-	margin-top: 1rem !important;
-}
-
-.go-to-cart {
-	max-height: 30px;
-	float: right;
-}
-
-.remove-wish {
-	background-color: white;
-	position: absolute;
-	cursor: pointer;
-	top:10px;
-	right: 20px;
-	width: 32px;
-	height: 32px;
-
-	border-radius: 50%;
-	border: 1px solid var(--gray-100);
-	box-shadow: 0px 2px 6px rgba(17, 43, 66, 0.08), 0px 1px 4px rgba(17, 43, 66, 0.1);
-}
-
-.wish-removed {
-	display: none;
-}
-
-.item-website-specification {
-	font-size: .875rem;
-	.product-title {
-		font-size: 18px;
-	}
-
-	.table {
-		width: 70%;
-	}
-
-	td {
-		border: none !important;
-	}
-
-	.spec-label {
-		color: var(--gray-600);
-	}
-
-	.spec-content {
-		color: var(--gray-800);
-	}
-}
-
-.reviews-full-page {
-	padding: 1rem 2rem;
-}
-
-.ratings-reviews-section {
-	border-top: 1px solid #E2E6E9;
-	padding: .5rem 1rem;
-}
-
-.reviews-header {
-	font-size: 20px;
-	font-weight: 600;
-	color: var(--gray-800);
-	display: flex;
-	align-items: center;
-	padding: 0;
-}
-
-.btn-write-review {
-	float: right;
-	padding: .5rem 1rem;
-	font-size: 14px;
-	font-weight: 400;
-	border: none !important;
-	box-shadow: none;
-
-	color: var(--gray-900);
-	background-color: var(--gray-100);
-
-	&:hover {
-		box-shadow: var(--btn-shadow);
-	}
-}
-
-.btn-view-more {
-	font-size: 14px;
-}
-
-.rating-summary-section {
-	display: flex;
-}
-
-.rating-summary-title {
-	margin-top: 0.15rem;
-	font-size: 18px;
-}
-
-.rating-summary-numbers {
-	display: flex;
-	flex-direction: column;
-	align-items: center;
-
-	border-right: solid 1px var(--gray-100);
-}
-
-.user-review-title {
-	margin-top: 0.15rem;
-	font-size: 15px;
-	font-weight: 600;
-}
-
-.rating {
-	--star-fill: var(--gray-300);
-	.star-hover {
-		--star-fill: var(--yellow-100);
-	}
-	.star-click {
-		--star-fill: var(--yellow-300);
-	}
-}
-
-.ratings-pill {
-	background-color: var(--gray-100);
-	padding: .5rem 1rem;
-	border-radius: 66px;
-}
-
-.review {
-	max-width: 80%;
-	line-height: 1.6;
-	padding-bottom: 0.5rem;
-	border-bottom: 1px solid #E2E6E9;
-}
-
-.review-signature {
-	display: flex;
-	font-size: 13px;
-	color: var(--gray-500);
-	font-weight: 400;
-
-	.reviewer {
-		padding-right: 8px;
-		color: var(--gray-600);
-	}
-}
-
-.rating-progress-bar-section {
-	padding-bottom: 2rem;
-
-	.rating-bar-title {
-		margin-left: -15px;
-	}
-
-	.rating-progress-bar {
-		margin-bottom: 4px;
-		height: 7px;
-		margin-top: 6px;
-
-		.progress-bar-cosmetic {
-			background-color: var(--gray-600);
-			border-radius: var(--border-radius);
-		}
-	}
-}
-
-.offer-container {
-	font-size: 14px;
-}
-
-#search-results-container {
-	border: 1px solid var(--gray-200);
-	padding: .25rem 1rem;
-
-	.category-chip {
-		background-color: var(--gray-100);
-		border: none !important;
-		box-shadow: none;
-	}
-
-	.recent-search {
-		padding: .5rem .5rem;
-		border-radius: var(--border-radius);
-
-		&:hover {
-			background-color: var(--gray-100);
-		}
-	}
-}
-
-#search-box {
-	background-color: white;
-	height: 100%;
-	padding-left: 2.5rem;
-	border: 1px solid var(--gray-200);
-}
-
-.search-icon {
-	position: absolute;
-	left: 0;
-	top: 0;
-	width: 2.5rem;
-	height: 100%;
-	display: flex;
-	justify-content: center;
-	align-items: center;
-	padding-bottom: 1px;
-}
-
-#toggle-view {
-	float: right;
-
-	.btn-primary {
-		background-color: var(--gray-600);
-		box-shadow: 0 0 0 0.2rem var(--gray-400);
-	}
-}
-
-.placeholder-div {
-	height:80%;
-	width: -webkit-fill-available;
-	padding: 50px;
-	text-align: center;
-	background-color: #F9FAFA;
-	border-top-left-radius: calc(0.75rem - 1px);
-	border-top-right-radius: calc(0.75rem - 1px);
-}
-.placeholder {
-	font-size: 72px;
-}
-
-[data-path="cart"] {
-	.modal-backdrop {
-		background-color: var(--gray-50); // lighter backdrop only on cart freeze
-	}
-}
-
-.item-thumb {
-	height: 50px;
-	max-width: 80px;
-	min-width: 80px;
-	object-fit: cover;
-}
-
-.brand-line {
-	color: gray;
-}
-
-.btn-next, .btn-prev {
-	font-size: 14px;
-}
-
-.alert-error {
-	color: #e27a84;
-	background-color: #fff6f7;
-	border-color: #f5c6cb;
-}
-
-.font-md {
-	font-size: 14px !important;
-}
-
-.in-green {
-	color: var(--green-info) !important;
-	font-weight: 500;
-}
-
-.has-stock {
-	font-weight: 400 !important;
-}
-
-.out-of-stock {
-	font-weight: 400;
-	font-size: 14px;
-	line-height: 20px;
-	color: #F47A7A;
-}
-
-.mt-minus-2 {
-	margin-top: -2rem;
-}
-
-.mt-minus-1 {
-	margin-top: -1rem;
-}
\ No newline at end of file
diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py
index a910af6..efeaeed 100644
--- a/erpnext/regional/united_arab_emirates/utils.py
+++ b/erpnext/regional/united_arab_emirates/utils.py
@@ -7,32 +7,32 @@
 
 
 def update_itemised_tax_data(doc):
+	# maybe this should be a standard function rather than a regional one
 	if not doc.taxes:
 		return
 
+	if not doc.items:
+		return
+
+	meta = frappe.get_meta(doc.items[0].doctype)
+	if not meta.has_field("tax_rate"):
+		return
+
 	itemised_tax = get_itemised_tax(doc.taxes)
 
 	for row in doc.items:
-		tax_rate = 0.0
-		item_tax_rate = 0.0
+		tax_rate, tax_amount = 0.0, 0.0
+		# dont even bother checking in item tax template as it contains both input and output accounts - double the tax rate
+		item_code = row.item_code or row.item_name
+		if itemised_tax.get(item_code):
+			for tax in itemised_tax.get(row.item_code).values():
+				_tax_rate = flt(tax.get("tax_rate", 0), row.precision("tax_rate"))
+				tax_amount += flt((row.net_amount * _tax_rate) / 100, row.precision("tax_amount"))
+				tax_rate += _tax_rate
 
-		if row.item_tax_rate:
-			item_tax_rate = frappe.parse_json(row.item_tax_rate)
-
-		# First check if tax rate is present
-		# If not then look up in item_wise_tax_detail
-		if item_tax_rate:
-			for account, rate in item_tax_rate.items():
-				tax_rate += rate
-		elif row.item_code and itemised_tax.get(row.item_code):
-			tax_rate = sum([tax.get("tax_rate", 0) for d, tax in itemised_tax.get(row.item_code).items()])
-
-		meta = frappe.get_meta(row.doctype)
-
-		if meta.has_field("tax_rate"):
-			row.tax_rate = flt(tax_rate, row.precision("tax_rate"))
-			row.tax_amount = flt((row.net_amount * tax_rate) / 100, row.precision("net_amount"))
-			row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount"))
+		row.tax_rate = flt(tax_rate, row.precision("tax_rate"))
+		row.tax_amount = flt(tax_amount, row.precision("tax_amount"))
+		row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount"))
 
 
 def get_account_currency(account):
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index 8ff681b..95d2d2c 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -26,7 +26,6 @@
 		self.set_status()
 		self.validate_uom_is_integer("stock_uom", "qty")
 		self.validate_valid_till()
-		self.validate_shopping_cart_items()
 		self.set_customer_name()
 		if self.items:
 			self.with_items = 1
@@ -42,26 +41,6 @@
 		if self.valid_till and getdate(self.valid_till) < getdate(self.transaction_date):
 			frappe.throw(_("Valid till date cannot be before transaction date"))
 
-	def validate_shopping_cart_items(self):
-		if self.order_type != "Shopping Cart":
-			return
-
-		for item in self.items:
-			has_web_item = frappe.db.exists("Website Item", {"item_code": item.item_code})
-
-			# If variant is unpublished but template is published: valid
-			template = frappe.get_cached_value("Item", item.item_code, "variant_of")
-			if template and not has_web_item:
-				has_web_item = frappe.db.exists("Website Item", {"item_code": template})
-
-			if not has_web_item:
-				frappe.throw(
-					_("Row #{0}: Item {1} must have a Website Item for Shopping Cart Quotations").format(
-						item.idx, frappe.bold(item.item_code)
-					),
-					title=_("Unpublished Item"),
-				)
-
 	def set_has_alternative_item(self):
 		"""Mark 'Has Alternative Item' for rows."""
 		if not any(row.is_alternative for row in self.get("items")):
@@ -263,8 +242,8 @@
 	return _make_sales_order(source_name, target_doc)
 
 
-def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
-	customer = _make_customer(source_name, ignore_permissions)
+def _make_sales_order(source_name, target_doc=None, customer_group=None, ignore_permissions=False):
+	customer = _make_customer(source_name, ignore_permissions, customer_group)
 	ordered_items = frappe._dict(
 		frappe.db.get_all(
 			"Sales Order Item",
@@ -428,7 +407,7 @@
 	return doclist
 
 
-def _make_customer(source_name, ignore_permissions=False):
+def _make_customer(source_name, ignore_permissions=False, customer_group=None):
 	quotation = frappe.db.get_value(
 		"Quotation", source_name, ["order_type", "party_name", "customer_name"], as_dict=1
 	)
@@ -445,10 +424,7 @@
 				customer_doclist = _make_customer(lead_name, ignore_permissions=ignore_permissions)
 				customer = frappe.get_doc(customer_doclist)
 				customer.flags.ignore_permissions = ignore_permissions
-				if quotation.get("party_name") == "Shopping Cart":
-					customer.customer_group = frappe.db.get_value(
-						"E Commerce Settings", None, "default_customer_group"
-					)
+				customer.customer_group = customer_group
 
 				try:
 					customer.insert()
diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py
index 5623a12..590cd3d 100644
--- a/erpnext/selling/doctype/quotation/test_quotation.py
+++ b/erpnext/selling/doctype/quotation/test_quotation.py
@@ -161,15 +161,6 @@
 
 		make_sales_order(quotation.name)
 
-	def test_shopping_cart_without_website_item(self):
-		if frappe.db.exists("Website Item", {"item_code": "_Test Item Home Desktop 100"}):
-			frappe.get_last_doc("Website Item", {"item_code": "_Test Item Home Desktop 100"}).delete()
-
-		quotation = frappe.copy_doc(test_records[0])
-		quotation.order_type = "Shopping Cart"
-		quotation.valid_till = getdate()
-		self.assertRaises(frappe.ValidationError, quotation.validate)
-
 	def test_create_quotation_with_margin(self):
 		from erpnext.selling.doctype.quotation.quotation import make_sales_order
 		from erpnext.selling.doctype.sales_order.sales_order import (
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index ba8bc33..3ad18da 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -87,17 +87,13 @@
 				frm.events.get_items_from_internal_purchase_order(frm);
 			}
 
-			if (frm.is_new()) {
+			if (frm.doc.docstatus === 0) {
 				frappe.db.get_single_value("Stock Settings", "enable_stock_reservation").then((value) => {
-					if (value) {
-						frappe.db.get_single_value("Stock Settings", "auto_reserve_stock_for_sales_order").then((value) => {
-							// If `Reserve Stock on Sales Order Submission` is enabled in Stock Settings, set Reserve Stock to 1 else 0.
-							frm.set_value("reserve_stock", value ? 1 : 0);
-						})
-					} else {
-						// If `Stock Reservation` is disabled in Stock Settings, set Reserve Stock to 0 and read only.
+					if (!value) {
+						// If `Stock Reservation` is disabled in Stock Settings, set Reserve Stock to 0 and make the field read-only and hidden.
 						frm.set_value("reserve_stock", 0);
 						frm.set_df_property("reserve_stock", "read_only", 1);
+						frm.set_df_property("reserve_stock", "hidden", 1);
 					}
 				})
 			}
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index 084537e..5b80dfd 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -1632,10 +1632,9 @@
   {
    "default": "0",
    "depends_on": "eval: (doc.docstatus == 0 || doc.reserve_stock)",
-   "description": "If checked, Stock Reservation Entries will be created on <b>Submit</b>",
+   "description": "If checked, Stock will be reserved on <b>Submit</b>",
    "fieldname": "reserve_stock",
    "fieldtype": "Check",
-   "hidden": 1,
    "label": "Reserve Stock",
    "no_copy": 1,
    "print_hide": 1,
@@ -1658,7 +1657,7 @@
  "idx": 105,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-10-10 13:36:07.526793",
+ "modified": "2023-10-18 12:41:54.813462",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 002ffe0..eb6d63e 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -3,6 +3,7 @@
 
 
 import json
+from typing import Literal
 
 import frappe
 import frappe.utils
@@ -536,14 +537,24 @@
 		return False
 
 	@frappe.whitelist()
-	def create_stock_reservation_entries(self, items_details=None, notify=True) -> None:
+	def create_stock_reservation_entries(
+		self,
+		items_details: list[dict] = None,
+		from_voucher_type: Literal["Pick List", "Purchase Receipt"] = None,
+		notify=True,
+	) -> None:
 		"""Creates Stock Reservation Entries for Sales Order Items."""
 
 		from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
 			create_stock_reservation_entries_for_so_items as create_stock_reservation_entries,
 		)
 
-		create_stock_reservation_entries(so=self, items_details=items_details, notify=notify)
+		create_stock_reservation_entries(
+			sales_order=self,
+			items_details=items_details,
+			from_voucher_type=from_voucher_type,
+			notify=notify,
+		)
 
 	@frappe.whitelist()
 	def cancel_stock_reservation_entries(self, sre_list=None, notify=True) -> None:
@@ -608,29 +619,37 @@
 
 
 def get_requested_item_qty(sales_order):
-	return frappe._dict(
-		frappe.db.sql(
-			"""
-		select sales_order_item, sum(qty)
-		from `tabMaterial Request Item`
-		where docstatus = 1
-			and sales_order = %s
-		group by sales_order_item
-	""",
-			sales_order,
-		)
-	)
+	result = {}
+	for d in frappe.db.get_all(
+		"Material Request Item",
+		filters={"docstatus": 1, "sales_order": sales_order},
+		fields=["sales_order_item", "sum(qty) as qty", "sum(received_qty) as received_qty"],
+		group_by="sales_order_item",
+	):
+		result[d.sales_order_item] = frappe._dict({"qty": d.qty, "received_qty": d.received_qty})
+
+	return result
 
 
 @frappe.whitelist()
 def make_material_request(source_name, target_doc=None):
 	requested_item_qty = get_requested_item_qty(source_name)
 
+	def get_remaining_qty(so_item):
+		return flt(
+			flt(so_item.qty)
+			- flt(requested_item_qty.get(so_item.name, {}).get("qty"))
+			- max(
+				flt(so_item.get("delivered_qty"))
+				- flt(requested_item_qty.get(so_item.name, {}).get("received_qty")),
+				0,
+			)
+		)
+
 	def update_item(source, target, source_parent):
 		# qty is for packed items, because packed items don't have stock_qty field
-		qty = source.get("qty")
 		target.project = source_parent.project
-		target.qty = qty - requested_item_qty.get(source.name, 0) - flt(source.get("delivered_qty"))
+		target.qty = get_remaining_qty(source)
 		target.stock_qty = flt(target.qty) * flt(target.conversion_factor)
 
 		args = target.as_dict().copy()
@@ -663,8 +682,8 @@
 			"Sales Order Item": {
 				"doctype": "Material Request Item",
 				"field_map": {"name": "sales_order_item", "parent": "sales_order"},
-				"condition": lambda doc: not frappe.db.exists("Product Bundle", doc.item_code)
-				and (doc.stock_qty - flt(doc.get("delivered_qty"))) > requested_item_qty.get(doc.name, 0),
+				"condition": lambda item: not frappe.db.exists("Product Bundle", item.item_code)
+				and get_remaining_qty(item) > 0,
 				"postprocess": update_item,
 			},
 		},
@@ -814,6 +833,7 @@
 							"postprocess": update_dn_item,
 						}
 					},
+					ignore_permissions=True,
 				)
 
 				dn_item.qty = flt(sre.reserved_qty) * flt(dn_item.get("conversion_factor", 1))
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 83689a2..d8b5878 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -1784,10 +1784,10 @@
 		si.submit()
 		pe.load_from_db()
 
-		self.assertEqual(pe.references[0].reference_name, si.name)
-		self.assertEqual(pe.references[0].allocated_amount, 200)
-		self.assertEqual(pe.references[1].reference_name, so.name)
-		self.assertEqual(pe.references[1].allocated_amount, 300)
+		self.assertEqual(pe.references[0].reference_name, so.name)
+		self.assertEqual(pe.references[0].allocated_amount, 300)
+		self.assertEqual(pe.references[1].reference_name, si.name)
+		self.assertEqual(pe.references[1].allocated_amount, 200)
 
 	def test_delivered_item_material_request(self):
 		"SO -> MR (Manufacture) -> WO. Test if WO Qty is updated in SO."
diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
index e6f7456..f82047f 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -68,6 +68,7 @@
   "total_weight",
   "column_break_21",
   "weight_uom",
+  "accounting_dimensions_section",
   "warehouse_and_reference",
   "warehouse",
   "target_warehouse",
@@ -889,12 +890,18 @@
    "label": "Production Plan Qty",
    "no_copy": 1,
    "read_only": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "accounting_dimensions_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Dimensions"
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-07-28 14:56:42.031636",
+ "modified": "2023-10-17 18:18:26.475259",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order Item",
@@ -905,4 +912,4 @@
  "sort_order": "DESC",
  "states": [],
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json
index 6855012..d6829ce 100644
--- a/erpnext/selling/doctype/selling_settings/selling_settings.json
+++ b/erpnext/selling/doctype/selling_settings/selling_settings.json
@@ -25,7 +25,7 @@
   "so_required",
   "dn_required",
   "sales_update_frequency",
-  "over_order_allowance",
+  "blanket_order_allowance",
   "column_break_5",
   "allow_multiple_items",
   "allow_against_multiple_purchase_orders",
@@ -184,12 +184,6 @@
    "label": "Allow Sales Order Creation For Expired Quotation"
   },
   {
-   "description": "Percentage you are allowed to order more against the Blanket Order Quantity. For example: If you have a Blanket Order of Quantity 100 units. and your Allowance is 10% then you are allowed to order 110 units.",
-   "fieldname": "over_order_allowance",
-   "fieldtype": "Float",
-   "label": "Over Order Allowance (%)"
-  },
-  {
    "default": "0",
    "fieldname": "dont_reserve_sales_order_qty_on_sales_return",
    "fieldtype": "Check",
@@ -200,6 +194,12 @@
    "fieldname": "allow_negative_rates_for_items",
    "fieldtype": "Check",
    "label": "Allow Negative rates for Items"
+  },
+  {
+   "description": "Percentage you are allowed to sell beyond the Blanket Order quantity.",
+   "fieldname": "blanket_order_allowance",
+   "fieldtype": "Float",
+   "label": "Blanket Order Allowance (%)"
   }
  ],
  "icon": "fa fa-cog",
@@ -207,7 +207,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2023-08-14 20:33:05.693667",
+ "modified": "2023-10-25 14:03:03.966701",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Selling Settings",
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index 4973dab..23b93dc 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -221,7 +221,6 @@
 		["cost_center", {}],
 		["round_off_cost_center", {}],
 		["depreciation_cost_center", {}],
-		["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"]]}],
@@ -236,8 +235,6 @@
 		$.each([
 			["stock_adjustment_account",
 				{"root_type": "Expense", "account_type": "Stock Adjustment"}],
-			["expenses_included_in_valuation",
-				{"root_type": "Expense", "account_type": "Expenses Included in Valuation"}],
 			["stock_received_but_not_billed",
 				{"root_type": "Liability", "account_type": "Stock Received But Not Billed"}],
 			["service_received_but_not_billed",
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index 24d7da4..b9ff3dd 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -80,7 +80,6 @@
   "accumulated_depreciation_account",
   "depreciation_expense_account",
   "series_for_depreciation_entry",
-  "expenses_included_in_asset_valuation",
   "column_break_40",
   "disposal_account",
   "depreciation_cost_center",
@@ -103,11 +102,10 @@
   "enable_provisional_accounting_for_non_stock_items",
   "default_inventory_account",
   "stock_adjustment_account",
-  "default_in_transit_warehouse",
   "column_break_32",
   "stock_received_but_not_billed",
   "default_provisional_account",
-  "expenses_included_in_valuation",
+  "default_in_transit_warehouse",
   "dashboard_tab"
  ],
  "fields": [
@@ -470,14 +468,6 @@
    "options": "Account"
   },
   {
-   "fieldname": "expenses_included_in_valuation",
-   "fieldtype": "Link",
-   "ignore_user_permissions": 1,
-   "label": "Expenses Included In Valuation",
-   "no_copy": 1,
-   "options": "Account"
-  },
-  {
    "fieldname": "accumulated_depreciation_account",
    "fieldtype": "Link",
    "label": "Accumulated Depreciation Account",
@@ -497,12 +487,6 @@
    "label": "Series for Asset Depreciation Entry (Journal Entry)"
   },
   {
-   "fieldname": "expenses_included_in_asset_valuation",
-   "fieldtype": "Link",
-   "label": "Expenses Included In Asset Valuation",
-   "options": "Account"
-  },
-  {
    "fieldname": "column_break_40",
    "fieldtype": "Column Break"
   },
@@ -782,7 +766,7 @@
  "image_field": "company_logo",
  "is_tree": 1,
  "links": [],
- "modified": "2023-09-10 21:53:13.860791",
+ "modified": "2023-10-23 10:19:24.322898",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Company",
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index b05696a..3413702 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -92,7 +92,6 @@
 			["Default Income Account", "default_income_account"],
 			["Stock Received But Not Billed Account", "stock_received_but_not_billed"],
 			["Stock Adjustment Account", "stock_adjustment_account"],
-			["Expense Included In Valuation Account", "expenses_included_in_valuation"],
 		]
 
 		for account in accounts:
@@ -384,7 +383,6 @@
 			"depreciation_expense_account": "Depreciation",
 			"capital_work_in_progress_account": "Capital Work in Progress",
 			"asset_received_but_not_billed": "Asset Received But Not Billed",
-			"expenses_included_in_asset_valuation": "Expenses Included In Asset Valuation",
 			"default_expense_account": "Cost of Goods Sold",
 		}
 
@@ -394,7 +392,6 @@
 					"stock_received_but_not_billed": "Stock Received But Not Billed",
 					"default_inventory_account": "Stock",
 					"stock_adjustment_account": "Stock Adjustment",
-					"expenses_included_in_valuation": "Expenses Included In Valuation",
 				}
 			)
 
diff --git a/erpnext/setup/doctype/driver/driver.json b/erpnext/setup/doctype/driver/driver.json
index 8d426cc..2e994b5 100644
--- a/erpnext/setup/doctype/driver/driver.json
+++ b/erpnext/setup/doctype/driver/driver.json
@@ -157,6 +157,22 @@
    "role": "HR Manager",
    "share": 1,
    "write": 1
+  },
+  {
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Delivery User"
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Delivery Manager",
+   "share": 1,
+   "write": 1
   }
  ],
  "quick_entry": 1,
@@ -166,4 +182,4 @@
  "sort_order": "DESC",
  "title_field": "full_name",
  "track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/setup/doctype/employee/employee.py b/erpnext/setup/doctype/employee/employee.py
index 566392c..78fb4df 100755
--- a/erpnext/setup/doctype/employee/employee.py
+++ b/erpnext/setup/doctype/employee/employee.py
@@ -123,7 +123,7 @@
 			user.gender = self.gender
 
 		if self.image:
-			if not user.user_image:
+			if not user.user_image or self.has_value_changed("image"):
 				user.user_image = self.image
 				try:
 					frappe.get_doc(
diff --git a/erpnext/setup/doctype/item_group/item_group.js b/erpnext/setup/doctype/item_group/item_group.js
index 4b04ac1..d6eb11f 100644
--- a/erpnext/setup/doctype/item_group/item_group.js
+++ b/erpnext/setup/doctype/item_group/item_group.js
@@ -71,20 +71,6 @@
 				frappe.set_route("List", "Item", {"item_group": frm.doc.name});
 			});
 		}
-
-		frappe.model.with_doctype('Website Item', () => {
-			const web_item_meta = frappe.get_meta('Website Item');
-
-			const valid_fields = web_item_meta.fields.filter(df =>
-				['Link', 'Table MultiSelect'].includes(df.fieldtype) && !df.hidden
-			).map(df =>
-				({ label: df.label, value: df.fieldname })
-			);
-
-			frm.get_field("filter_fields").grid.update_docfield_property(
-				'fieldname', 'options', valid_fields
-			);
-		});
 	},
 
 	set_root_readonly: function(frm) {
diff --git a/erpnext/setup/doctype/item_group/item_group.json b/erpnext/setup/doctype/item_group/item_group.json
index e0f5090..dfa5a8e 100644
--- a/erpnext/setup/doctype/item_group/item_group.json
+++ b/erpnext/setup/doctype/item_group/item_group.json
@@ -19,22 +19,9 @@
   "item_group_defaults",
   "sec_break_taxes",
   "taxes",
-  "sb9",
-  "route",
-  "website_title",
-  "description",
-  "show_in_website",
-  "include_descendants",
-  "column_break_16",
-  "weightage",
-  "slideshow",
-  "website_specifications",
-  "website_filters_section",
-  "filter_fields",
-  "filter_attributes",
   "lft",
-  "rgt",
-  "old_parent"
+  "old_parent",
+  "rgt"
  ],
  "fields": [
   {
@@ -107,54 +94,6 @@
    "options": "Item Tax"
   },
   {
-   "fieldname": "sb9",
-   "fieldtype": "Section Break",
-   "label": "Website Settings"
-  },
-  {
-   "default": "0",
-   "description": "Make Item Group visible in website",
-   "fieldname": "show_in_website",
-   "fieldtype": "Check",
-   "label": "Show in Website"
-  },
-  {
-   "depends_on": "show_in_website",
-   "fieldname": "route",
-   "fieldtype": "Data",
-   "label": "Route",
-   "no_copy": 1,
-   "unique": 1
-  },
-  {
-   "depends_on": "show_in_website",
-   "fieldname": "weightage",
-   "fieldtype": "Int",
-   "label": "Weightage"
-  },
-  {
-   "depends_on": "show_in_website",
-   "description": "Show this slideshow at the top of the page",
-   "fieldname": "slideshow",
-   "fieldtype": "Link",
-   "label": "Slideshow",
-   "options": "Website Slideshow"
-  },
-  {
-   "depends_on": "show_in_website",
-   "description": "HTML / Banner that will show on the top of product list.",
-   "fieldname": "description",
-   "fieldtype": "Text Editor",
-   "label": "Description"
-  },
-  {
-   "depends_on": "show_in_website",
-   "fieldname": "website_specifications",
-   "fieldtype": "Table",
-   "label": "Website Specifications",
-   "options": "Item Website Specification"
-  },
-  {
    "fieldname": "lft",
    "fieldtype": "Int",
    "hidden": 1,
@@ -188,43 +127,6 @@
    "options": "Item Group",
    "print_hide": 1,
    "report_hide": 1
-  },
-  {
-   "collapsible": 1,
-   "depends_on": "show_in_website",
-   "fieldname": "website_filters_section",
-   "fieldtype": "Section Break",
-   "label": "Website Filters"
-  },
-  {
-   "fieldname": "filter_fields",
-   "fieldtype": "Table",
-   "label": "Item Fields",
-   "options": "Website Filter Field"
-  },
-  {
-   "fieldname": "filter_attributes",
-   "fieldtype": "Table",
-   "label": "Attributes",
-   "options": "Website Attribute"
-  },
-  {
-   "depends_on": "show_in_website",
-   "fieldname": "website_title",
-   "fieldtype": "Data",
-   "label": "Title"
-  },
-  {
-   "fieldname": "column_break_16",
-   "fieldtype": "Column Break"
-  },
-  {
-   "default": "0",
-   "depends_on": "show_in_website",
-   "description": "Include Website Items belonging to child Item Groups",
-   "fieldname": "include_descendants",
-   "fieldtype": "Check",
-   "label": "Include Descendants"
   }
  ],
  "icon": "fa fa-sitemap",
@@ -233,7 +135,7 @@
  "is_tree": 1,
  "links": [],
  "max_attachments": 3,
- "modified": "2023-08-28 22:27:48.382985",
+ "modified": "2023-10-12 13:44:13.611287",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Item Group",
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index cc67c69..fe7a241 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -2,39 +2,19 @@
 # License: GNU General Public License v3. See license.txt
 
 import copy
-from urllib.parse import quote
 
 import frappe
 from frappe import _
-from frappe.utils import cint
 from frappe.utils.nestedset import NestedSet
-from frappe.website.utils import clear_cache
-from frappe.website.website_generator import WebsiteGenerator
-
-from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import ECommerceSettings
-from erpnext.e_commerce.product_data_engine.filters import ProductFiltersBuilder
 
 
-class ItemGroup(NestedSet, WebsiteGenerator):
-	nsm_parent_field = "parent_item_group"
-	website = frappe._dict(
-		condition_field="show_in_website",
-		template="templates/generators/item_group.html",
-		no_cache=1,
-		no_breadcrumbs=1,
-	)
-
+class ItemGroup(NestedSet):
 	def validate(self):
-		super(ItemGroup, self).validate()
-
 		if not self.parent_item_group and not frappe.flags.in_test:
 			if frappe.db.exists("Item Group", _("All Item Groups")):
 				self.parent_item_group = _("All Item Groups")
-
-		self.make_route()
 		self.validate_item_group_defaults()
 		self.check_item_tax()
-		ECommerceSettings.validate_field_filters(self.filter_fields, enable_field_filters=True)
 
 	def check_item_tax(self):
 		"""Check whether Tax Rate is not entered twice for same Tax Type"""
@@ -53,66 +33,13 @@
 
 	def on_update(self):
 		NestedSet.on_update(self)
-		invalidate_cache_for(self)
 		self.validate_one_root()
 		self.delete_child_item_groups_key()
 
-	def make_route(self):
-		"""Make website route"""
-		if not self.route:
-			self.route = ""
-			if self.parent_item_group:
-				parent_item_group = frappe.get_doc("Item Group", self.parent_item_group)
-
-				# make parent route only if not root
-				if parent_item_group.parent_item_group and parent_item_group.route:
-					self.route = parent_item_group.route + "/"
-
-			self.route += self.scrub(self.item_group_name)
-
-			return self.route
-
 	def on_trash(self):
 		NestedSet.on_trash(self, allow_root_deletion=True)
-		WebsiteGenerator.on_trash(self)
 		self.delete_child_item_groups_key()
 
-	def get_context(self, context):
-		context.show_search = True
-		context.body_class = "product-page"
-		context.page_length = (
-			cint(frappe.db.get_single_value("E Commerce Settings", "products_per_page")) or 6
-		)
-		context.search_link = "/product_search"
-
-		filter_engine = ProductFiltersBuilder(self.name)
-
-		context.field_filters = filter_engine.get_field_filters()
-		context.attribute_filters = filter_engine.get_attribute_filters()
-
-		context.update({"parents": get_parent_item_groups(self.parent_item_group), "title": self.name})
-
-		if self.slideshow:
-			values = {"show_indicators": 1, "show_controls": 0, "rounded": 1, "slider_name": self.slideshow}
-			slideshow = frappe.get_doc("Website Slideshow", self.slideshow)
-			slides = slideshow.get({"doctype": "Website Slideshow Item"})
-			for index, slide in enumerate(slides):
-				values[f"slide_{index + 1}_image"] = slide.image
-				values[f"slide_{index + 1}_title"] = slide.heading
-				values[f"slide_{index + 1}_subtitle"] = slide.description
-				values[f"slide_{index + 1}_theme"] = slide.get("theme") or "Light"
-				values[f"slide_{index + 1}_content_align"] = slide.get("content_align") or "Centre"
-				values[f"slide_{index + 1}_primary_action"] = slide.url
-
-			context.slideshow = values
-
-		context.no_breadcrumbs = False
-		context.title = self.website_title or self.name
-		context.name = self.name
-		context.item_group_name = self.item_group_name
-
-		return context
-
 	def delete_child_item_groups_key(self):
 		frappe.cache().hdel("child_item_groups", self.name)
 
@@ -122,20 +49,6 @@
 		validate_item_default_company_links(self.item_group_defaults)
 
 
-def get_child_groups_for_website(item_group_name, immediate=False, include_self=False):
-	"""Returns child item groups *excluding* passed group."""
-	item_group = frappe.get_cached_value("Item Group", item_group_name, ["lft", "rgt"], as_dict=1)
-	filters = {"lft": [">", item_group.lft], "rgt": ["<", item_group.rgt], "show_in_website": 1}
-
-	if immediate:
-		filters["parent_item_group"] = item_group_name
-
-	if include_self:
-		filters.update({"lft": [">=", item_group.lft], "rgt": ["<=", item_group.rgt]})
-
-	return frappe.get_all("Item Group", filters=filters, fields=["name", "route"], order_by="name")
-
-
 def get_child_item_groups(item_group_name):
 	item_group = frappe.get_cached_value("Item Group", item_group_name, ["lft", "rgt"], as_dict=1)
 
@@ -149,63 +62,6 @@
 	return child_item_groups or {}
 
 
-def get_item_for_list_in_html(context):
-	# add missing absolute link in files
-	# user may forget it during upload
-	if (context.get("website_image") or "").startswith("files/"):
-		context["website_image"] = "/" + quote(context["website_image"])
-
-	products_template = "templates/includes/products_as_list.html"
-
-	return frappe.get_template(products_template).render(context)
-
-
-def get_parent_item_groups(item_group_name, from_item=False):
-	settings = frappe.get_cached_doc("E Commerce Settings")
-
-	if settings.enable_field_filters:
-		base_nav_page = {"name": _("Shop by Category"), "route": "/shop-by-category"}
-	else:
-		base_nav_page = {"name": _("All Products"), "route": "/all-products"}
-
-	if from_item and frappe.request.environ.get("HTTP_REFERER"):
-		# base page after 'Home' will vary on Item page
-		last_page = frappe.request.environ["HTTP_REFERER"].split("/")[-1].split("?")[0]
-		if last_page and last_page in ("shop-by-category", "all-products"):
-			base_nav_page_title = " ".join(last_page.split("-")).title()
-			base_nav_page = {"name": _(base_nav_page_title), "route": "/" + last_page}
-
-	base_parents = [
-		{"name": _("Home"), "route": "/"},
-		base_nav_page,
-	]
-
-	if not item_group_name:
-		return base_parents
-
-	item_group = frappe.db.get_value("Item Group", item_group_name, ["lft", "rgt"], as_dict=1)
-	parent_groups = frappe.db.sql(
-		"""select name, route from `tabItem Group`
-		where lft <= %s and rgt >= %s
-		and show_in_website=1
-		order by lft asc""",
-		(item_group.lft, item_group.rgt),
-		as_dict=True,
-	)
-
-	return base_parents + parent_groups
-
-
-def invalidate_cache_for(doc, item_group=None):
-	if not item_group:
-		item_group = doc.name
-
-	for d in get_parent_item_groups(item_group):
-		item_group_name = frappe.db.get_value("Item Group", d.get("name"))
-		if item_group_name:
-			clear_cache(frappe.db.get_value("Item Group", item_group_name, "route"))
-
-
 def get_item_group_defaults(item, company):
 	item = frappe.get_cached_doc("Item", item)
 	item_group = frappe.get_cached_doc("Item Group", item.item_group)
diff --git a/erpnext/setup/doctype/vehicle/vehicle.json b/erpnext/setup/doctype/vehicle/vehicle.json
index ed803a7..b19d459 100644
--- a/erpnext/setup/doctype/vehicle/vehicle.json
+++ b/erpnext/setup/doctype/vehicle/vehicle.json
@@ -860,6 +860,22 @@
    "share": 1,
    "submit": 0,
    "write": 1
+  },
+  {
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Delivery User"
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Delivery Manager",
+   "share": 1,
+   "write": 1
   }
  ],
  "quick_entry": 1,
@@ -872,4 +888,4 @@
  "title_field": "",
  "track_changes": 1,
  "track_seen": 0
-}
\ No newline at end of file
+}
diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py
index 85eaf5f..b106cfc 100644
--- a/erpnext/setup/install.py
+++ b/erpnext/setup/install.py
@@ -33,6 +33,7 @@
 	add_app_name()
 	setup_log_settings()
 	hide_workspaces()
+	update_roles()
 	frappe.db.commit()
 
 
@@ -232,6 +233,12 @@
 		frappe.db.set_value("Workspace", ws, "public", 0)
 
 
+def update_roles():
+	website_user_roles = ("Customer", "Supplier")
+	for role in website_user_roles:
+		frappe.db.set_value("Role", role, "desk_access", 0)
+
+
 def create_default_role_profiles():
 	for role_profile_name, roles in DEFAULT_ROLE_PROFILES.items():
 		role_profile = frappe.new_doc("Role Profile")
diff --git a/erpnext/setup/setup_wizard/operations/company_setup.py b/erpnext/setup/setup_wizard/operations/company_setup.py
index ace5cca..d4aac5e 100644
--- a/erpnext/setup/setup_wizard/operations/company_setup.py
+++ b/erpnext/setup/setup_wizard/operations/company_setup.py
@@ -33,20 +33,6 @@
 		).insert()
 
 
-def enable_shopping_cart(args):  # nosemgrep
-	# Needs price_lists
-	frappe.get_doc(
-		{
-			"doctype": "E Commerce Settings",
-			"enabled": 1,
-			"company": args.get("company_name"),
-			"price_list": frappe.db.get_value("Price List", {"selling": 1}),
-			"default_customer_group": _("Individual"),
-			"quotation_series": "QTN-",
-		}
-	).insert()
-
-
 def get_fy_details(fy_start_date, fy_end_date):
 	start_year = getdate(fy_start_date).year
 	if start_year == getdate(fy_end_date).year:
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index ae6881b..2205924 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -454,7 +454,6 @@
 
 	set_global_defaults(args)
 	update_stock_settings()
-	update_shopping_cart_settings(args)
 
 	args.update({"set_default": 1})
 	create_bank_account(args)
@@ -529,20 +528,6 @@
 			pass
 
 
-def update_shopping_cart_settings(args):  # nosemgrep
-	shopping_cart = frappe.get_doc("E Commerce Settings")
-	shopping_cart.update(
-		{
-			"enabled": 1,
-			"company": args.company_name,
-			"price_list": frappe.db.get_value("Price List", {"selling": 1}),
-			"default_customer_group": _("Individual"),
-			"quotation_series": "QTN-",
-		}
-	)
-	shopping_cart.update_single(shopping_cart.get_valid_dict())
-
-
 def get_fy_details(fy_start_date, fy_end_date):
 	start_year = getdate(fy_start_date).year
 	if start_year == getdate(fy_end_date).year:
diff --git a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
index 5806fd1..2f9cec4 100644
--- a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
+++ b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
@@ -1,500 +1,500 @@
 {
- "charts": [],
- "content": "[{\"id\":\"NO5yYHJopc\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t</b></span>\",\"col\":12}},{\"id\":\"CDxIM-WuZ9\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"System Settings\",\"col\":3}},{\"id\":\"-Uh7DKJNJX\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Settings\",\"col\":3}},{\"id\":\"K9ST9xcDXh\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Settings\",\"col\":3}},{\"id\":\"27IdVHVQMb\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Selling Settings\",\"col\":3}},{\"id\":\"Rwp5zff88b\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Buying Settings\",\"col\":3}},{\"id\":\"hkfnQ2sevf\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Global Defaults\",\"col\":3}},{\"id\":\"jjxI_PDawD\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Print Settings\",\"col\":3}},{\"id\":\"R3CoYYFXye\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"yynbm1J_VO\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Settings</b></span>\",\"col\":12}},{\"id\":\"KDCv2MvSg3\",\"type\":\"card\",\"data\":{\"card_name\":\"Module Settings\",\"col\":4}},{\"id\":\"Q0_bqT7cxQ\",\"type\":\"card\",\"data\":{\"card_name\":\"Email / Notifications\",\"col\":4}},{\"id\":\"UnqK5haBnh\",\"type\":\"card\",\"data\":{\"card_name\":\"Website\",\"col\":4}},{\"id\":\"kp7u1H5hCd\",\"type\":\"card\",\"data\":{\"card_name\":\"Core\",\"col\":4}},{\"id\":\"Ufc3jycgy9\",\"type\":\"card\",\"data\":{\"card_name\":\"Printing\",\"col\":4}},{\"id\":\"89bSNzv3Yh\",\"type\":\"card\",\"data\":{\"card_name\":\"Workflow\",\"col\":4}}]",
- "creation": "2022-01-27 13:14:47.349433",
- "custom_blocks": [],
- "docstatus": 0,
- "doctype": "Workspace",
- "for_user": "",
- "hide_custom": 0,
- "icon": "setting",
- "idx": 0,
- "is_hidden": 0,
- "label": "ERPNext Settings",
- "links": [
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Import Data",
-   "link_count": 0,
-   "link_to": "Data Import",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Export Data",
-   "link_count": 0,
-   "link_to": "Data Export",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Bulk Update",
-   "link_count": 0,
-   "link_to": "Bulk Update",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Download Backups",
-   "link_count": 0,
-   "link_to": "backups",
-   "link_type": "Page",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Deleted Documents",
-   "link_count": 0,
-   "link_to": "Deleted Document",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Email / Notifications",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Email Account",
-   "link_count": 0,
-   "link_to": "Email Account",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Email Domain",
-   "link_count": 0,
-   "link_to": "Email Domain",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Notification",
-   "link_count": 0,
-   "link_to": "Notification",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Email Template",
-   "link_count": 0,
-   "link_to": "Email Template",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Auto Email Report",
-   "link_count": 0,
-   "link_to": "Auto Email Report",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Newsletter",
-   "link_count": 0,
-   "link_to": "Newsletter",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Notification Settings",
-   "link_count": 0,
-   "link_to": "Notification Settings",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Website",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Website Settings",
-   "link_count": 0,
-   "link_to": "Website Settings",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Website Theme",
-   "link_count": 0,
-   "link_to": "Website Theme",
-   "link_type": "DocType",
-   "onboard": 1,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Website Script",
-   "link_count": 0,
-   "link_to": "Website Script",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "About Us Settings",
-   "link_count": 0,
-   "link_to": "About Us Settings",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Contact Us Settings",
-   "link_count": 0,
-   "link_to": "Contact Us Settings",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Printing",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Print Format Builder",
-   "link_count": 0,
-   "link_to": "print-format-builder",
-   "link_type": "Page",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Print Settings",
-   "link_count": 0,
-   "link_to": "Print Settings",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Print Format",
-   "link_count": 0,
-   "link_to": "Print Format",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Print Style",
-   "link_count": 0,
-   "link_to": "Print Style",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Workflow",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Workflow",
-   "link_count": 0,
-   "link_to": "Workflow",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Workflow State",
-   "link_count": 0,
-   "link_to": "Workflow State",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Workflow Action",
-   "link_count": 0,
-   "link_to": "Workflow Action",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Core",
-   "link_count": 3,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "System Settings",
-   "link_count": 0,
-   "link_to": "System Settings",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "dependencies": "",
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Domain Settings",
-   "link_count": 0,
-   "link_to": "Domain Settings",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Global Defaults",
-   "link_count": 0,
-   "link_to": "Global Defaults",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Module Settings",
-   "link_count": 8,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Accounts Settings",
-   "link_count": 0,
-   "link_to": "Accounts Settings",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Stock Settings",
-   "link_count": 0,
-   "link_to": "Stock Settings",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Selling Settings",
-   "link_count": 0,
-   "link_to": "Selling Settings",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Buying Settings",
-   "link_count": 0,
-   "link_to": "Buying Settings",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Manufacturing Settings",
-   "link_count": 0,
-   "link_to": "Manufacturing Settings",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "CRM Settings",
-   "link_count": 0,
-   "link_to": "CRM Settings",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Projects Settings",
-   "link_count": 0,
-   "link_to": "Projects Settings",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Support Settings",
-   "link_count": 0,
-   "link_to": "Support Settings",
-   "link_type": "DocType",
-   "onboard": 0,
-   "type": "Link"
-  }
- ],
- "modified": "2023-05-24 14:47:25.356531",
- "modified_by": "Administrator",
- "module": "Setup",
- "name": "ERPNext Settings",
- "number_cards": [],
- "owner": "Administrator",
- "parent_page": "",
- "public": 1,
- "quick_lists": [],
- "restrict_to_domain": "",
- "roles": [],
- "sequence_id": 19.0,
- "shortcuts": [
-  {
-   "color": "Grey",
-   "doc_view": "List",
-   "label": "Print Settings",
-   "link_to": "Print Settings",
-   "type": "DocType"
-  },
-  {
-   "color": "Grey",
-   "doc_view": "List",
-   "label": "System Settings",
-   "link_to": "System Settings",
-   "type": "DocType"
-  },
-  {
-   "icon": "accounting",
-   "label": "Accounts Settings",
-   "link_to": "Accounts Settings",
-   "type": "DocType"
-  },
-  {
-   "color": "Grey",
-   "doc_view": "List",
-   "label": "Global Defaults",
-   "link_to": "Global Defaults",
-   "type": "DocType"
-  },
-  {
-   "icon": "stock",
-   "label": "Stock Settings",
-   "link_to": "Stock Settings",
-   "type": "DocType"
-  },
-  {
-   "icon": "sell",
-   "label": "Selling Settings",
-   "link_to": "Selling Settings",
-   "type": "DocType"
-  },
-  {
-   "icon": "buying",
-   "label": "Buying Settings",
-   "link_to": "Buying Settings",
-   "type": "DocType"
-  }
- ],
- "title": "ERPNext Settings"
-}
\ No newline at end of file
+   "charts": [],
+   "content": "[{\"id\":\"NO5yYHJopc\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t</b></span>\",\"col\":12}},{\"id\":\"CDxIM-WuZ9\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"System Settings\",\"col\":3}},{\"id\":\"-Uh7DKJNJX\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Settings\",\"col\":3}},{\"id\":\"K9ST9xcDXh\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Settings\",\"col\":3}},{\"id\":\"27IdVHVQMb\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Selling Settings\",\"col\":3}},{\"id\":\"Rwp5zff88b\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Buying Settings\",\"col\":3}},{\"id\":\"hkfnQ2sevf\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Global Defaults\",\"col\":3}},{\"id\":\"jjxI_PDawD\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Print Settings\",\"col\":3}},{\"id\":\"R3CoYYFXye\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"yynbm1J_VO\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Settings</b></span>\",\"col\":12}},{\"id\":\"KDCv2MvSg3\",\"type\":\"card\",\"data\":{\"card_name\":\"Module Settings\",\"col\":4}},{\"id\":\"Q0_bqT7cxQ\",\"type\":\"card\",\"data\":{\"card_name\":\"Email / Notifications\",\"col\":4}},{\"id\":\"UnqK5haBnh\",\"type\":\"card\",\"data\":{\"card_name\":\"Website\",\"col\":4}},{\"id\":\"kp7u1H5hCd\",\"type\":\"card\",\"data\":{\"card_name\":\"Core\",\"col\":4}},{\"id\":\"Ufc3jycgy9\",\"type\":\"card\",\"data\":{\"card_name\":\"Printing\",\"col\":4}},{\"id\":\"89bSNzv3Yh\",\"type\":\"card\",\"data\":{\"card_name\":\"Workflow\",\"col\":4}}]",
+   "creation": "2022-01-27 13:14:47.349433",
+   "custom_blocks": [],
+   "docstatus": 0,
+   "doctype": "Workspace",
+   "for_user": "",
+   "hide_custom": 0,
+   "icon": "setting",
+   "idx": 0,
+   "is_hidden": 0,
+   "label": "ERPNext Settings",
+   "links": [
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Import Data",
+     "link_count": 0,
+     "link_to": "Data Import",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Export Data",
+     "link_count": 0,
+     "link_to": "Data Export",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Bulk Update",
+     "link_count": 0,
+     "link_to": "Bulk Update",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Download Backups",
+     "link_count": 0,
+     "link_to": "backups",
+     "link_type": "Page",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Deleted Documents",
+     "link_count": 0,
+     "link_to": "Deleted Document",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Email / Notifications",
+     "link_count": 0,
+     "onboard": 0,
+     "type": "Card Break"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Email Account",
+     "link_count": 0,
+     "link_to": "Email Account",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Email Domain",
+     "link_count": 0,
+     "link_to": "Email Domain",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Notification",
+     "link_count": 0,
+     "link_to": "Notification",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Email Template",
+     "link_count": 0,
+     "link_to": "Email Template",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Auto Email Report",
+     "link_count": 0,
+     "link_to": "Auto Email Report",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Newsletter",
+     "link_count": 0,
+     "link_to": "Newsletter",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Notification Settings",
+     "link_count": 0,
+     "link_to": "Notification Settings",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Website",
+     "link_count": 0,
+     "onboard": 0,
+     "type": "Card Break"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Website Settings",
+     "link_count": 0,
+     "link_to": "Website Settings",
+     "link_type": "DocType",
+     "onboard": 1,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Website Theme",
+     "link_count": 0,
+     "link_to": "Website Theme",
+     "link_type": "DocType",
+     "onboard": 1,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Website Script",
+     "link_count": 0,
+     "link_to": "Website Script",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "About Us Settings",
+     "link_count": 0,
+     "link_to": "About Us Settings",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Contact Us Settings",
+     "link_count": 0,
+     "link_to": "Contact Us Settings",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Printing",
+     "link_count": 0,
+     "onboard": 0,
+     "type": "Card Break"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Print Format Builder",
+     "link_count": 0,
+     "link_to": "print-format-builder",
+     "link_type": "Page",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Print Settings",
+     "link_count": 0,
+     "link_to": "Print Settings",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Print Format",
+     "link_count": 0,
+     "link_to": "Print Format",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Print Style",
+     "link_count": 0,
+     "link_to": "Print Style",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Workflow",
+     "link_count": 0,
+     "onboard": 0,
+     "type": "Card Break"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Workflow",
+     "link_count": 0,
+     "link_to": "Workflow",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Workflow State",
+     "link_count": 0,
+     "link_to": "Workflow State",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Workflow Action",
+     "link_count": 0,
+     "link_to": "Workflow Action",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Core",
+     "link_count": 3,
+     "onboard": 0,
+     "type": "Card Break"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "System Settings",
+     "link_count": 0,
+     "link_to": "System Settings",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "dependencies": "",
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Domain Settings",
+     "link_count": 0,
+     "link_to": "Domain Settings",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Global Defaults",
+     "link_count": 0,
+     "link_to": "Global Defaults",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Module Settings",
+     "link_count": 8,
+     "onboard": 0,
+     "type": "Card Break"
+    },
+    {
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Accounts Settings",
+     "link_count": 0,
+     "link_to": "Accounts Settings",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Stock Settings",
+     "link_count": 0,
+     "link_to": "Stock Settings",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Selling Settings",
+     "link_count": 0,
+     "link_to": "Selling Settings",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Buying Settings",
+     "link_count": 0,
+     "link_to": "Buying Settings",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Manufacturing Settings",
+     "link_count": 0,
+     "link_to": "Manufacturing Settings",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "CRM Settings",
+     "link_count": 0,
+     "link_to": "CRM Settings",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Projects Settings",
+     "link_count": 0,
+     "link_to": "Projects Settings",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    },
+    {
+     "hidden": 0,
+     "is_query_report": 0,
+     "label": "Support Settings",
+     "link_count": 0,
+     "link_to": "Support Settings",
+     "link_type": "DocType",
+     "onboard": 0,
+     "type": "Link"
+    }
+   ],
+   "modified": "2023-05-24 14:47:25.356531",
+   "modified_by": "Administrator",
+   "module": "Setup",
+   "name": "ERPNext Settings",
+   "number_cards": [],
+   "owner": "Administrator",
+   "parent_page": "",
+   "public": 1,
+   "quick_lists": [],
+   "restrict_to_domain": "",
+   "roles": [],
+   "sequence_id": 19.0,
+   "shortcuts": [
+    {
+     "color": "Grey",
+     "doc_view": "List",
+     "label": "Print Settings",
+     "link_to": "Print Settings",
+     "type": "DocType"
+    },
+    {
+     "color": "Grey",
+     "doc_view": "List",
+     "label": "System Settings",
+     "link_to": "System Settings",
+     "type": "DocType"
+    },
+    {
+     "icon": "accounting",
+     "label": "Accounts Settings",
+     "link_to": "Accounts Settings",
+     "type": "DocType"
+    },
+    {
+     "color": "Grey",
+     "doc_view": "List",
+     "label": "Global Defaults",
+     "link_to": "Global Defaults",
+     "type": "DocType"
+    },
+    {
+     "icon": "stock",
+     "label": "Stock Settings",
+     "link_to": "Stock Settings",
+     "type": "DocType"
+    },
+    {
+     "icon": "sell",
+     "label": "Selling Settings",
+     "link_to": "Selling Settings",
+     "type": "DocType"
+    },
+    {
+     "icon": "buying",
+     "label": "Buying Settings",
+     "link_to": "Buying Settings",
+     "type": "DocType"
+    }
+   ],
+   "title": "ERPNext Settings"
+  }
\ No newline at end of file
diff --git a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py
index 295d979..b0499bf 100644
--- a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py
+++ b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py
@@ -1,6 +1,6 @@
 # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
-
+import gzip
 import json
 
 import frappe
@@ -8,7 +8,7 @@
 from frappe.core.doctype.prepared_report.prepared_report import create_json_gz_file
 from frappe.desk.form.load import get_attachments
 from frappe.model.document import Document
-from frappe.utils import get_link_to_form, gzip_decompress, parse_json
+from frappe.utils import get_link_to_form, parse_json
 from frappe.utils.background_jobs import enqueue
 
 from erpnext.stock.report.stock_balance.stock_balance import execute
@@ -109,7 +109,7 @@
 			attachment = attachments[0]
 			attached_file = frappe.get_doc("File", attachment.name)
 
-			data = gzip_decompress(attached_file.get_content())
+			data = gzip.decompress(attached_file.get_content())
 			if data := json.loads(data.decode("utf-8")):
 				data = data
 
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index e0d4919..b85f296 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -1460,6 +1460,36 @@
    "read": 1,
    "role": "Stock Manager",
    "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Delivery User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Delivery Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
   }
  ],
  "search_fields": "status,customer,customer_name, territory,base_grand_total",
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 48b8ab7..1eecf6d 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -1230,6 +1230,21 @@
 		frappe.db.rollback()
 		frappe.db.set_single_value("Selling Settings", "dont_reserve_sales_order_qty_on_sales_return", 0)
 
+	def test_non_internal_transfer_delivery_note(self):
+		from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+
+		dn = create_delivery_note(do_not_submit=True)
+		warehouse = create_warehouse("Internal Transfer Warehouse", company=dn.company)
+		dn.items[0].db_set("target_warehouse", warehouse)
+
+		dn.reload()
+
+		self.assertEqual(dn.items[0].target_warehouse, warehouse)
+
+		dn.save()
+		dn.reload()
+		self.assertFalse(dn.items[0].target_warehouse)
+
 
 def create_delivery_note(**args):
 	dn = frappe.new_doc("Delivery Note")
diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
index 612d674..6148950 100644
--- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
+++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
@@ -725,7 +725,8 @@
    "label": "Against Delivery Note Item",
    "no_copy": 1,
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "search_index": 1
   },
   {
    "fieldname": "stock_qty_sec_break",
@@ -892,7 +893,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-07-26 12:53:49.357171",
+ "modified": "2023-10-16 16:18:18.013379",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note Item",
@@ -902,4 +903,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "states": []
-}
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/delivery_settings/delivery_settings.json b/erpnext/stock/doctype/delivery_settings/delivery_settings.json
index 963403b..ad0ac45 100644
--- a/erpnext/stock/doctype/delivery_settings/delivery_settings.json
+++ b/erpnext/stock/doctype/delivery_settings/delivery_settings.json
@@ -239,7 +239,7 @@
    "print": 1, 
    "read": 1, 
    "report": 0, 
-   "role": "System Manager", 
+   "role": "Delivery Manager",
    "set_user_permissions": 0, 
    "share": 1, 
    "submit": 0, 
@@ -255,4 +255,4 @@
  "track_changes": 1, 
  "track_seen": 0, 
  "track_views": 0
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/delivery_stop/delivery_stop.json b/erpnext/stock/doctype/delivery_stop/delivery_stop.json
index 5610a81..42560e6 100644
--- a/erpnext/stock/doctype/delivery_stop/delivery_stop.json
+++ b/erpnext/stock/doctype/delivery_stop/delivery_stop.json
@@ -1,815 +1,197 @@
 {
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2017-10-16 16:46:28.166950", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "actions": [],
+ "creation": "2017-10-16 16:46:28.166950",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "customer",
+  "address",
+  "locked",
+  "column_break_6",
+  "customer_address",
+  "visited",
+  "order_information_section",
+  "delivery_note",
+  "cb_order",
+  "grand_total",
+  "section_break_7",
+  "contact",
+  "email_sent_to",
+  "column_break_7",
+  "customer_contact",
+  "section_break_9",
+  "distance",
+  "estimated_arrival",
+  "lat",
+  "column_break_19",
+  "uom",
+  "lng",
+  "more_information_section",
+  "details"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 2, 
-   "fieldname": "customer", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Customer", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Customer", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "columns": 2,
+   "fieldname": "customer",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Customer",
+   "options": "Customer"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "address", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Address Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Address", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "address",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Address Name",
+   "options": "Address",
+   "print_hide": 1,
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "lock", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Lock", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "default": "0",
+   "fieldname": "locked",
+   "fieldtype": "Check",
+   "in_list_view": 1,
+   "label": "Locked"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_6", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_6",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "customer_address", 
-   "fieldtype": "Small Text", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Customer Address", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "customer_address",
+   "fieldtype": "Small Text",
+   "label": "Customer Address",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 1, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:doc.docstatus==1", 
-   "fieldname": "visited", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Visited", 
-   "length": 0, 
-   "no_copy": 1, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "allow_on_submit": 1,
+   "default": "0",
+   "depends_on": "eval:doc.docstatus==1",
+   "fieldname": "visited",
+   "fieldtype": "Check",
+   "label": "Visited",
+   "no_copy": 1,
+   "print_hide": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "order_information_section", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Order Information", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "order_information_section",
+   "fieldtype": "Section Break",
+   "label": "Order Information"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "delivery_note", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Delivery Note", 
-   "length": 0, 
-   "no_copy": 1, 
-   "options": "Delivery Note", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "delivery_note",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Delivery Note",
+   "no_copy": 1,
+   "options": "Delivery Note",
+   "print_hide": 1,
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "cb_order", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "cb_order",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "grand_total", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Grand Total", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "grand_total",
+   "fieldtype": "Currency",
+   "label": "Grand Total",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_7", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Contact Information", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_7",
+   "fieldtype": "Section Break",
+   "label": "Contact Information"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "contact", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Contact Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Contact", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "contact",
+   "fieldtype": "Link",
+   "label": "Contact Name",
+   "options": "Contact",
+   "print_hide": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "email_sent_to", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Email sent to", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "email_sent_to",
+   "fieldtype": "Data",
+   "label": "Email sent to",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_7", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_7",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "customer_contact", 
-   "fieldtype": "Small Text", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Customer Contact", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "customer_contact",
+   "fieldtype": "Small Text",
+   "label": "Customer Contact",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_9", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Dispatch Information", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_9",
+   "fieldtype": "Section Break",
+   "label": "Dispatch Information"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "distance", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Distance", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "2", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "distance",
+   "fieldtype": "Float",
+   "label": "Distance",
+   "precision": "2",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "estimated_arrival", 
-   "fieldtype": "Datetime", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Estimated Arrival", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "estimated_arrival",
+   "fieldtype": "Datetime",
+   "in_list_view": 1,
+   "label": "Estimated Arrival"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "lat", 
-   "fieldtype": "Float", 
-   "hidden": 1, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Latitude", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "lat",
+   "fieldtype": "Float",
+   "hidden": 1,
+   "label": "Latitude"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_19", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_19",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "", 
-   "depends_on": "eval:doc.distance", 
-   "fieldname": "uom", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "UOM", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "UOM", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "eval:doc.distance",
+   "fieldname": "uom",
+   "fieldtype": "Link",
+   "label": "UOM",
+   "options": "UOM",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "lng", 
-   "fieldtype": "Float", 
-   "hidden": 1, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Longitude", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "lng",
+   "fieldtype": "Float",
+   "hidden": 1,
+   "label": "Longitude"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "more_information_section", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "More Information", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "more_information_section",
+   "fieldtype": "Section Break",
+   "label": "More Information"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "details", 
-   "fieldtype": "Text Editor", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Details", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
+   "fieldname": "details",
+   "fieldtype": "Text Editor",
+   "label": "Details"
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2018-10-16 05:23:25.661542", 
- "modified_by": "Administrator", 
- "module": "Stock", 
- "name": "Delivery Stop", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0, 
- "track_views": 0
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2023-09-29 09:22:53.435161",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Delivery Stop",
+ "owner": "Administrator",
+ "permissions": [],
+ "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/delivery_trip/delivery_trip.js b/erpnext/stock/doctype/delivery_trip/delivery_trip.js
index de503dc..158bd0c 100755
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.js
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.js
@@ -62,8 +62,13 @@
 						company: frm.doc.company,
 					}
 				})
-			}, __("Get customers from"));
+			}, __("Get stops from"));
 		}
+		frm.add_custom_button(__("Delivery Notes"), function () {
+			frappe.set_route("List", "Delivery Note",
+					{'name': ["in", frm.doc.delivery_stops.map((stop) => {return stop.delivery_note;})]}
+			);
+		}, __("View"));
 	},
 
 	calculate_arrival_time: function (frm) {
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.json b/erpnext/stock/doctype/delivery_trip/delivery_trip.json
index 9d8fe46..ec72af8 100644
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.json
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.json
@@ -188,7 +188,7 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2023-06-27 11:22:27.927637",
+ "modified": "2023-10-01 07:06:06.314503",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Trip",
@@ -224,10 +224,40 @@
    "share": 1,
    "submit": 1,
    "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Delivery User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Delivery Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
   }
  ],
  "sort_field": "modified",
  "sort_order": "DESC",
  "states": [],
  "title_field": "driver_name"
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.py b/erpnext/stock/doctype/delivery_trip/delivery_trip.py
index af2f411..c531a87 100644
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.py
@@ -170,7 +170,7 @@
 		for stop in self.delivery_stops:
 			leg.append(stop.customer_address)
 
-			if optimize and stop.lock:
+			if optimize and stop.locked:
 				route_list.append(leg)
 				leg = [stop.customer_address]
 
diff --git a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
index ed699e3..9b8b46e 100644
--- a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
@@ -46,7 +46,7 @@
 		self.assertEqual(len(route_list[0]), 4)
 
 	def test_unoptimized_route_list_with_locks(self):
-		self.delivery_trip.delivery_stops[0].lock = 1
+		self.delivery_trip.delivery_stops[0].locked = 1
 		self.delivery_trip.save()
 		route_list = self.delivery_trip.form_route_list(optimize=False)
 
@@ -65,7 +65,7 @@
 		self.assertEqual(len(route_list[0]), 4)
 
 	def test_optimized_route_list_with_locks(self):
-		self.delivery_trip.delivery_stops[0].lock = 1
+		self.delivery_trip.delivery_stops[0].locked = 1
 		self.delivery_trip.save()
 		route_list = self.delivery_trip.form_route_list(optimize=True)
 
diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js
index 0310682..35d1c02 100644
--- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js
+++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js
@@ -37,7 +37,7 @@
 		if (frm.doc.__onload && frm.doc.__onload.has_stock_ledger
 			&& frm.doc.__onload.has_stock_ledger.length) {
 			let allow_to_edit_fields = ['disabled', 'fetch_from_parent',
-				'type_of_transaction', 'condition', 'mandatory_depends_on'];
+				'type_of_transaction', 'condition', 'mandatory_depends_on', 'validate_negative_stock'];
 
 			frm.fields.forEach((field) => {
 				if (!in_list(allow_to_edit_fields, field.df.fieldname)) {
diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json
index eb6102a..0e40552 100644
--- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json
+++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json
@@ -17,6 +17,8 @@
   "target_fieldname",
   "applicable_for_documents_tab",
   "apply_to_all_doctypes",
+  "column_break_niy2u",
+  "validate_negative_stock",
   "column_break_13",
   "document_type",
   "type_of_transaction",
@@ -173,11 +175,21 @@
    "fieldname": "reqd",
    "fieldtype": "Check",
    "label": "Mandatory"
+  },
+  {
+   "fieldname": "column_break_niy2u",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "validate_negative_stock",
+   "fieldtype": "Check",
+   "label": "Validate Negative Stock"
   }
  ],
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2023-01-31 13:44:38.507698",
+ "modified": "2023-10-05 12:52:18.705431",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Inventory Dimension",
diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py
index 8bff4d5..257d18f 100644
--- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py
+++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py
@@ -60,6 +60,7 @@
 			"fetch_from_parent",
 			"type_of_transaction",
 			"condition",
+			"validate_negative_stock",
 		]
 
 		for field in frappe.get_meta("Inventory Dimension").fields:
@@ -160,6 +161,7 @@
 				insert_after="inventory_dimension",
 				options=self.reference_document,
 				label=label,
+				search_index=1,
 				reqd=self.reqd,
 				mandatory_depends_on=self.mandatory_depends_on,
 			),
@@ -255,7 +257,7 @@
 def get_inventory_documents(
 	doctype=None, txt=None, searchfield=None, start=None, page_len=None, filters=None
 ):
-	and_filters = [["DocField", "parent", "not in", ["Batch", "Serial No"]]]
+	and_filters = [["DocField", "parent", "not in", ["Batch", "Serial No", "Item Price"]]]
 	or_filters = [
 		["DocField", "options", "in", ["Batch", "Serial No"]],
 		["DocField", "parent", "in", ["Putaway Rule"]],
@@ -340,6 +342,7 @@
 			fields=[
 				"distinct target_fieldname as fieldname",
 				"reference_document as doctype",
+				"validate_negative_stock",
 			],
 			filters={"disabled": 0},
 		)
diff --git a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py
index 2d273c6..33394e5 100644
--- a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py
+++ b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py
@@ -414,6 +414,53 @@
 			else:
 				self.assertEqual(d.store, "Inter Transfer Store 2")
 
+	def test_validate_negative_stock_for_inventory_dimension(self):
+		frappe.local.inventory_dimensions = {}
+		item_code = "Test Negative Inventory Dimension Item"
+		frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 1)
+		create_item(item_code)
+
+		inv_dimension = create_inventory_dimension(
+			apply_to_all_doctypes=1,
+			dimension_name="Inv Site",
+			reference_document="Inv Site",
+			document_type="Inv Site",
+			validate_negative_stock=1,
+		)
+
+		warehouse = create_warehouse("Negative Stock Warehouse")
+		doc = make_stock_entry(item_code=item_code, target=warehouse, qty=10, do_not_submit=True)
+
+		doc.items[0].to_inv_site = "Site 1"
+		doc.submit()
+
+		site_name = frappe.get_all(
+			"Stock Ledger Entry", filters={"voucher_no": doc.name, "is_cancelled": 0}, fields=["inv_site"]
+		)[0].inv_site
+
+		self.assertEqual(site_name, "Site 1")
+
+		doc = make_stock_entry(item_code=item_code, source=warehouse, qty=100, do_not_submit=True)
+
+		doc.items[0].inv_site = "Site 1"
+		self.assertRaises(frappe.ValidationError, doc.submit)
+
+		inv_dimension.reload()
+		inv_dimension.db_set("validate_negative_stock", 0)
+		frappe.local.inventory_dimensions = {}
+
+		doc = make_stock_entry(item_code=item_code, source=warehouse, qty=100, do_not_submit=True)
+
+		doc.items[0].inv_site = "Site 1"
+		doc.submit()
+		self.assertEqual(doc.docstatus, 1)
+
+		site_name = frappe.get_all(
+			"Stock Ledger Entry", filters={"voucher_no": doc.name, "is_cancelled": 0}, fields=["inv_site"]
+		)[0].inv_site
+
+		self.assertEqual(site_name, "Site 1")
+
 
 def get_voucher_sl_entries(voucher_no, fields):
 	return frappe.get_all(
@@ -504,6 +551,26 @@
 			}
 		).insert(ignore_permissions=True)
 
+	if not frappe.db.exists("DocType", "Inv Site"):
+		frappe.get_doc(
+			{
+				"doctype": "DocType",
+				"name": "Inv Site",
+				"module": "Stock",
+				"custom": 1,
+				"naming_rule": "By fieldname",
+				"autoname": "field:site_name",
+				"fields": [{"label": "Site Name", "fieldname": "site_name", "fieldtype": "Data"}],
+				"permissions": [
+					{"role": "System Manager", "permlevel": 0, "read": 1, "write": 1, "create": 1, "delete": 1}
+				],
+			}
+		).insert(ignore_permissions=True)
+
+	for site in ["Site 1", "Site 2"]:
+		if not frappe.db.exists("Inv Site", site):
+			frappe.get_doc({"doctype": "Inv Site", "site_name": site}).insert(ignore_permissions=True)
+
 
 def create_inventory_dimension(**args):
 	args = frappe._dict(args)
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index 4ae9bf5..6e810e5 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -125,36 +125,6 @@
 			erpnext.toggle_naming_series();
 		}
 
-		if (!frm.doc.published_in_website) {
-			frm.add_custom_button(__("Publish in Website"), function() {
-				frappe.call({
-					method: "erpnext.e_commerce.doctype.website_item.website_item.make_website_item",
-					args: {doc: frm.doc},
-					freeze: true,
-					freeze_message: __("Publishing Item ..."),
-					callback: function(result) {
-						frappe.msgprint({
-							message: __("Website Item {0} has been created.",
-								[repl('<a href="/app/website-item/%(item_encoded)s" class="strong">%(item)s</a>', {
-									item_encoded: encodeURIComponent(result.message[0]),
-									item: result.message[1]
-								})]
-							),
-							title: __("Published"),
-							indicator: "green"
-						});
-					}
-				});
-			}, __('Actions'));
-		} else {
-			frm.add_custom_button(__("Website Item"), function() {
-				frappe.db.get_value("Website Item", {item_code: frm.doc.name}, "name", (d) => {
-					if (!d.name) frappe.throw(__("Website Item not found"));
-					frappe.set_route("Form", "Website Item", d.name);
-				});
-			}, __("View"));
-		}
-
 		erpnext.item.edit_prices_button(frm);
 		erpnext.item.toggle_attributes(frm);
 
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index 1bcddfa..c13d3eb 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -117,7 +117,6 @@
   "customer_code",
   "default_item_manufacturer",
   "default_manufacturer_part_no",
-  "published_in_website",
   "total_projected_qty"
  ],
  "fields": [
@@ -380,7 +379,7 @@
    "options": "fa fa-rss"
   },
   {
-   "description": "Will also apply for variants unless overrridden",
+   "description": "Will also apply for variants unless overridden",
    "fieldname": "reorder_levels",
    "fieldtype": "Table",
    "label": "Reorder level based on Warehouse",
@@ -816,14 +815,6 @@
    "read_only": 1
   },
   {
-   "default": "0",
-   "depends_on": "published_in_website",
-   "fieldname": "published_in_website",
-   "fieldtype": "Check",
-   "label": "Published in Website",
-   "read_only": 1
-  },
-  {
    "default": "1",
    "fieldname": "grant_commission",
    "fieldtype": "Check",
@@ -970,4 +961,4 @@
  "states": [],
  "title_field": "item_name",
  "track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index aff9587..d8935fe 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -32,7 +32,6 @@
 	make_variant_item_code,
 	validate_item_variant_attributes,
 )
-from erpnext.setup.doctype.item_group.item_group import invalidate_cache_for
 from erpnext.stock.doctype.item_default.item_default import ItemDefault
 
 
@@ -122,10 +121,8 @@
 			self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
 
 	def on_update(self):
-		invalidate_cache_for_item(self)
 		self.update_variants()
 		self.update_item_price()
-		self.update_website_item()
 
 	def validate_description(self):
 		"""Clean HTML description if set"""
@@ -248,29 +245,6 @@
 		if self.stock_uom not in uoms_list:
 			self.append("uoms", {"uom": self.stock_uom, "conversion_factor": 1})
 
-	def update_website_item(self):
-		"""Update Website Item if change in Item impacts it."""
-		web_item = frappe.db.exists("Website Item", {"item_code": self.item_code})
-
-		if web_item:
-			changed = {}
-			editable_fields = ["item_name", "item_group", "stock_uom", "brand", "description", "disabled"]
-			doc_before_save = self.get_doc_before_save()
-
-			for field in editable_fields:
-				if doc_before_save.get(field) != self.get(field):
-					if field == "disabled":
-						changed["published"] = not self.get(field)
-					else:
-						changed[field] = self.get(field)
-
-			if not changed:
-				return
-
-			web_item_doc = frappe.get_doc("Website Item", web_item)
-			web_item_doc.update(changed)
-			web_item_doc.save()
-
 	def validate_item_tax_net_rate_range(self):
 		for tax in self.get("taxes"):
 			if flt(tax.maximum_net_rate) < flt(tax.minimum_net_rate):
@@ -281,7 +255,7 @@
 
 		# add item taxes from template
 		for d in template.get("taxes"):
-			self.append("taxes", {"item_tax_template": d.item_tax_template})
+			self.append("taxes", d)
 
 		# copy re-order table if empty
 		if not self.get("reorder_levels"):
@@ -454,7 +428,6 @@
 		if merge:
 			self.validate_properties_before_merge(new_name)
 			self.validate_duplicate_product_bundles_before_merge(old_name, new_name)
-			self.validate_duplicate_website_item_before_merge(old_name, new_name)
 			self.delete_old_bins(old_name)
 
 	def after_rename(self, old_name, new_name, merge):
@@ -466,9 +439,6 @@
 				title=_("Note"),
 			)
 
-		if self.published_in_website:
-			invalidate_cache_for_item(self)
-
 		frappe.db.set_value("Item", new_name, "item_code", new_name)
 
 		if merge:
@@ -554,27 +524,6 @@
 			)
 			frappe.throw(msg, title=_("Cannot Merge"), exc=DataValidationError)
 
-	def validate_duplicate_website_item_before_merge(self, old_name, new_name):
-		"""
-		Block merge if both old and new items have website items against them.
-		This is to avoid duplicate website items after merging.
-		"""
-		web_items = frappe.get_all(
-			"Website Item",
-			filters={"item_code": ["in", [old_name, new_name]]},
-			fields=["item_code", "name"],
-		)
-
-		if len(web_items) <= 1:
-			return
-
-		old_web_item = [d.get("name") for d in web_items if d.get("item_code") == old_name][0]
-		web_item_link = get_link_to_form("Website Item", old_web_item)
-		old_name, new_name = frappe.bold(old_name), frappe.bold(new_name)
-
-		msg = f"Please delete linked Website Item {frappe.bold(web_item_link)} before merging {old_name} into {new_name}"
-		frappe.throw(_(msg), title=_("Cannot Merge"), exc=DataValidationError)
-
 	def set_last_purchase_rate(self, new_name):
 		last_purchase_rate = get_last_purchase_details(new_name).get("base_net_rate", 0)
 		frappe.db.set_value("Item", new_name, "last_purchase_rate", last_purchase_rate)
@@ -1151,32 +1100,6 @@
 	return out
 
 
-def invalidate_cache_for_item(doc):
-	"""Invalidate Item Group cache and rebuild ItemVariantsCacheManager."""
-	invalidate_cache_for(doc, doc.item_group)
-
-	if doc.get("old_item_group") and doc.get("old_item_group") != doc.item_group:
-		invalidate_cache_for(doc, doc.old_item_group)
-
-	invalidate_item_variants_cache_for_website(doc)
-
-
-def invalidate_item_variants_cache_for_website(doc):
-	"""Rebuild ItemVariantsCacheManager via Item or Website Item."""
-	from erpnext.e_commerce.variant_selector.item_variants_cache import ItemVariantsCacheManager
-
-	item_code = None
-	is_web_item = doc.get("published_in_website") or doc.get("published")
-	if doc.has_variants and is_web_item:
-		item_code = doc.item_code
-	elif doc.variant_of and frappe.db.get_value("Item", doc.variant_of, "published_in_website"):
-		item_code = doc.variant_of
-
-	if item_code:
-		item_cache = ItemVariantsCacheManager(item_code)
-		item_cache.rebuild_cache()
-
-
 def check_stock_uom_with_bin(item, stock_uom):
 	if stock_uom == frappe.db.get_value("Item", item, "stock_uom"):
 		return
diff --git a/erpnext/stock/doctype/item/item_dashboard.py b/erpnext/stock/doctype/item/item_dashboard.py
index 34bb4d1..88ae34f 100644
--- a/erpnext/stock/doctype/item/item_dashboard.py
+++ b/erpnext/stock/doctype/item/item_dashboard.py
@@ -32,6 +32,5 @@
 			{"label": _("Manufacture"), "items": ["Production Plan", "Work Order", "Item Manufacturer"]},
 			{"label": _("Traceability"), "items": ["Serial No", "Batch"]},
 			{"label": _("Stock Movement"), "items": ["Stock Entry", "Stock Reconciliation"]},
-			{"label": _("E-commerce"), "items": ["Website Item"]},
 		],
 	}
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index 0c6dc77..09d3dd1 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -163,7 +163,7 @@
 			{
 				"item_code": "_Test Item With Item Tax Template",
 				"tax_category": "_Test Tax Category 2",
-				"item_tax_template": None,
+				"item_tax_template": "",
 			},
 			{
 				"item_code": "_Test Item Inherit Group Item Tax Template 1",
@@ -178,7 +178,7 @@
 			{
 				"item_code": "_Test Item Inherit Group Item Tax Template 1",
 				"tax_category": "_Test Tax Category 2",
-				"item_tax_template": None,
+				"item_tax_template": "",
 			},
 			{
 				"item_code": "_Test Item Inherit Group Item Tax Template 2",
@@ -193,7 +193,7 @@
 			{
 				"item_code": "_Test Item Inherit Group Item Tax Template 2",
 				"tax_category": "_Test Tax Category 2",
-				"item_tax_template": None,
+				"item_tax_template": "",
 			},
 			{
 				"item_code": "_Test Item Override Group Item Tax Template",
@@ -208,12 +208,12 @@
 			{
 				"item_code": "_Test Item Override Group Item Tax Template",
 				"tax_category": "_Test Tax Category 2",
-				"item_tax_template": None,
+				"item_tax_template": "",
 			},
 		]
 
 		expected_item_tax_map = {
-			None: {},
+			"": {},
 			"_Test Account Excise Duty @ 10 - _TC": {"_Test Account Excise Duty - _TC": 10},
 			"_Test Account Excise Duty @ 12 - _TC": {"_Test Account Excise Duty - _TC": 12},
 			"_Test Account Excise Duty @ 15 - _TC": {"_Test Account Excise Duty - _TC": 15},
@@ -907,6 +907,8 @@
 	opening_stock=0,
 	is_fixed_asset=0,
 	asset_category=None,
+	buying_cost_center=None,
+	selling_cost_center=None,
 	company="_Test Company",
 ):
 	if not frappe.db.exists("Item", item_code):
@@ -924,7 +926,15 @@
 		item.is_purchase_item = is_purchase_item
 		item.is_customer_provided_item = is_customer_provided_item
 		item.customer = customer or ""
-		item.append("item_defaults", {"default_warehouse": warehouse, "company": company})
+		item.append(
+			"item_defaults",
+			{
+				"default_warehouse": warehouse,
+				"company": company,
+				"selling_cost_center": selling_cost_center,
+				"buying_cost_center": buying_cost_center,
+			},
+		)
 		item.save()
 	else:
 		item = frappe.get_doc("Item", item_code)
diff --git a/erpnext/stock/doctype/item_price/item_price.js b/erpnext/stock/doctype/item_price/item_price.js
index ce489ff..8a4b4ee 100644
--- a/erpnext/stock/doctype/item_price/item_price.js
+++ b/erpnext/stock/doctype/item_price/item_price.js
@@ -6,7 +6,6 @@
 		frm.set_query("item_code", function() {
 			return {
 				filters: {
-					"disabled": 0,
 					"has_variants": 0
 				}
 			};
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index bf3301f..9673a70 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -102,6 +102,12 @@
 
 		if (frm.doc.docstatus == 1 && frm.doc.status != 'Stopped') {
 			let precision = frappe.defaults.get_default("float_precision");
+
+			if (flt(frm.doc.per_received, precision) < 100) {
+				frm.add_custom_button(__('Stop'),
+					() => frm.events.update_status(frm, 'Stopped'));
+			}
+
 			if (flt(frm.doc.per_ordered, precision) < 100) {
 				let add_create_pick_list_button = () => {
 					frm.add_custom_button(__('Pick List'),
@@ -148,11 +154,6 @@
 				}
 
 				frm.page.set_inner_btn_group_as_primary(__('Create'));
-
-				// stop
-				frm.add_custom_button(__('Stop'),
-					() => frm.events.update_status(frm, 'Stopped'));
-
 			}
 		}
 
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index a51028d..ecdec80 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -401,6 +401,7 @@
 					["uom", "uom"],
 					["sales_order", "sales_order"],
 					["sales_order_item", "sales_order_item"],
+					["wip_composite_asset", "wip_composite_asset"],
 				],
 				"postprocess": update_item,
 				"condition": select_item,
diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json
index c585d6c..9912be1 100644
--- a/erpnext/stock/doctype/material_request_item/material_request_item.json
+++ b/erpnext/stock/doctype/material_request_item/material_request_item.json
@@ -37,6 +37,10 @@
   "rate",
   "col_break3",
   "amount",
+  "accounting_details_section",
+  "expense_account",
+  "column_break_glru",
+  "wip_composite_asset",
   "manufacture_details",
   "manufacturer",
   "manufacturer_part_no",
@@ -50,11 +54,10 @@
   "lead_time_date",
   "sales_order",
   "sales_order_item",
+  "col_break4",
   "production_plan",
   "material_request_plan_item",
   "job_card_item",
-  "col_break4",
-  "expense_account",
   "section_break_46",
   "page_break"
  ],
@@ -454,13 +457,28 @@
    "label": "Job Card Item",
    "no_copy": 1,
    "print_hide": 1
+  },
+  {
+   "fieldname": "accounting_details_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Details"
+  },
+  {
+   "fieldname": "column_break_glru",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "wip_composite_asset",
+   "fieldtype": "Link",
+   "label": "WIP Composite Asset",
+   "options": "Asset"
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-05-07 20:23:31.250252",
+ "modified": "2023-10-27 15:53:41.444236",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Material Request Item",
@@ -471,4 +489,4 @@
  "sort_order": "DESC",
  "states": [],
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js
index ae05b80..7cd171e 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.js
+++ b/erpnext/stock/doctype/pick_list/pick_list.js
@@ -265,7 +265,8 @@
 			from_date: moment(frm.doc.creation).format('YYYY-MM-DD'),
 			to_date: to_date,
 			voucher_type: "Sales Order",
-			against_pick_list: frm.doc.name,
+			from_voucher_type: "Pick List",
+			from_voucher_no: frm.doc.name,
 		}
 		frappe.set_route("query-report", "Reserved Stock");
 	}
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index 2fcd102..ed20209 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -229,20 +229,27 @@
 	def create_stock_reservation_entries(self, notify=True) -> None:
 		"""Creates Stock Reservation Entries for Sales Order Items against Pick List."""
 
-		from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
-			create_stock_reservation_entries_for_so_items,
-		)
-
-		so_details = {}
+		so_items_details_map = {}
 		for location in self.locations:
 			if location.warehouse and location.sales_order and location.sales_order_item:
-				so_details.setdefault(location.sales_order, []).append(location)
+				item_details = {
+					"name": location.sales_order_item,
+					"item_code": location.item_code,
+					"warehouse": location.warehouse,
+					"qty_to_reserve": (flt(location.picked_qty) - flt(location.stock_reserved_qty)),
+					"from_voucher_no": location.parent,
+					"from_voucher_detail_no": location.name,
+					"serial_and_batch_bundle": location.serial_and_batch_bundle,
+				}
+				so_items_details_map.setdefault(location.sales_order, []).append(item_details)
 
-		if so_details:
-			for so, locations in so_details.items():
+		if so_items_details_map:
+			for so, items_details in so_items_details_map.items():
 				so_doc = frappe.get_doc("Sales Order", so)
-				create_stock_reservation_entries_for_so_items(
-					so=so_doc, items_details=locations, against_pick_list=True, notify=notify
+				so_doc.create_stock_reservation_entries(
+					items_details=items_details,
+					from_voucher_type="Pick List",
+					notify=notify,
 				)
 
 	@frappe.whitelist()
@@ -253,7 +260,9 @@
 			cancel_stock_reservation_entries,
 		)
 
-		cancel_stock_reservation_entries(against_pick_list=self.name, notify=notify)
+		cancel_stock_reservation_entries(
+			from_voucher_type="Pick List", from_voucher_no=self.name, notify=notify
+		)
 
 	def validate_picked_qty(self, data):
 		over_delivery_receipt_allowance = 100 + flt(
diff --git a/erpnext/stock/doctype/pick_list/pick_list_dashboard.py b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py
index 0830fa2..29571a5 100644
--- a/erpnext/stock/doctype/pick_list/pick_list_dashboard.py
+++ b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py
@@ -2,7 +2,7 @@
 	return {
 		"fieldname": "pick_list",
 		"non_standard_fieldnames": {
-			"Stock Reservation Entry": "against_pick_list",
+			"Stock Reservation Entry": "from_voucher_no",
 		},
 		"internal_links": {
 			"Sales Order": ["locations", "sales_order"],
diff --git a/erpnext/stock/doctype/price_list/price_list.py b/erpnext/stock/doctype/price_list/price_list.py
index e77d53a..21c0f18 100644
--- a/erpnext/stock/doctype/price_list/price_list.py
+++ b/erpnext/stock/doctype/price_list/price_list.py
@@ -13,9 +13,6 @@
 		if not cint(self.buying) and not cint(self.selling):
 			throw(_("Price List must be applicable for Buying or Selling"))
 
-		if not self.is_new():
-			self.check_impact_on_shopping_cart()
-
 	def on_update(self):
 		self.set_default_if_missing()
 		self.update_item_price()
@@ -37,19 +34,6 @@
 			(self.currency, cint(self.buying), cint(self.selling), self.name),
 		)
 
-	def check_impact_on_shopping_cart(self):
-		"Check if Price List currency change impacts E Commerce Cart."
-		from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import (
-			validate_cart_settings,
-		)
-
-		doc_before_save = self.get_doc_before_save()
-		currency_changed = self.currency != doc_before_save.currency
-		affects_cart = self.name == frappe.db.get_single_value("E Commerce Settings", "price_list")
-
-		if currency_changed and affects_cart:
-			validate_cart_settings()
-
 	def on_trash(self):
 		self.delete_price_list_details_key()
 
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 6afa86e..2a4b6f3 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -13,7 +13,6 @@
 import erpnext
 from erpnext.accounts.utils import get_account_currency
 from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_enabled
-from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
 from erpnext.buying.utils import check_on_hold_or_closed_status
 from erpnext.controllers.buying_controller import BuyingController
 from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_transaction
@@ -144,8 +143,8 @@
 			if item.is_fixed_asset and is_cwip_accounting_enabled(item.asset_category):
 				# check cwip accounts before making auto assets
 				# Improves UX by not giving messages of "Assets Created" before throwing error of not finding arbnb account
-				arbnb_account = self.get_company_default("asset_received_but_not_billed")
-				cwip_account = get_asset_account(
+				self.get_company_default("asset_received_but_not_billed")
+				get_asset_account(
 					"capital_work_in_progress_account", asset_category=item.asset_category, company=self.company
 				)
 				break
@@ -264,6 +263,7 @@
 		self.make_gl_entries()
 		self.repost_future_sle_and_gle()
 		self.set_consumed_qty_in_subcontract_order()
+		self.reserve_stock_for_sales_order()
 
 	def check_next_docstatus(self):
 		submit_rv = frappe.db.sql(
@@ -313,7 +313,7 @@
 
 		self.make_item_gl_entries(gl_entries, warehouse_account=warehouse_account)
 		self.make_tax_gl_entries(gl_entries)
-		self.get_asset_gl_entry(gl_entries)
+		update_regional_gl_entries(gl_entries, self)
 
 		return process_gl_map(gl_entries)
 
@@ -322,14 +322,6 @@
 			get_purchase_document_details,
 		)
 
-		stock_rbnb = None
-		if erpnext.is_perpetual_inventory_enabled(self.company):
-			stock_rbnb = self.get_company_default("stock_received_but_not_billed")
-			landed_cost_entries = get_item_account_wise_additional_cost(self.name)
-			expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
-
-		warehouse_with_no_account = []
-		stock_items = self.get_stock_items()
 		provisional_accounting_for_non_stock_items = cint(
 			frappe.db.get_value(
 				"Company", self.company, "enable_provisional_accounting_for_non_stock_items"
@@ -338,28 +330,258 @@
 
 		exchange_rate_map, net_rate_map = get_purchase_document_details(self)
 
+		def validate_account(account_type):
+			frappe.throw(_("{0} account not found while submitting purchase receipt").format(account_type))
+
+		def make_item_asset_inward_gl_entry(item, stock_value_diff, stock_asset_account_name):
+			account_currency = get_account_currency(stock_asset_account_name)
+
+			if not stock_asset_account_name:
+				validate_account("Asset or warehouse account")
+
+			self.add_gl_entry(
+				gl_entries=gl_entries,
+				account=stock_asset_account_name,
+				cost_center=d.cost_center,
+				debit=stock_value_diff,
+				credit=0.0,
+				remarks=remarks,
+				against_account=stock_asset_rbnb,
+				account_currency=account_currency,
+				item=item,
+			)
+
+		def make_stock_received_but_not_billed_entry(item):
+			account = (
+				warehouse_account[item.from_warehouse]["account"] if item.from_warehouse else stock_asset_rbnb
+			)
+			account_currency = get_account_currency(account)
+
+			# GL Entry for from warehouse or Stock Received but not billed
+			# Intentionally passed negative debit amount to avoid incorrect GL Entry validation
+			credit_amount = (
+				flt(item.base_net_amount, item.precision("base_net_amount"))
+				if account_currency == self.company_currency
+				else flt(item.net_amount, item.precision("net_amount"))
+			)
+
+			outgoing_amount = item.base_net_amount
+			if self.is_internal_transfer() and item.valuation_rate:
+				outgoing_amount = abs(get_stock_value_difference(self.name, item.name, item.from_warehouse))
+				credit_amount = outgoing_amount
+
+			if credit_amount:
+				if not account:
+					validate_account("Stock or Asset Received But Not Billed")
+
+				self.add_gl_entry(
+					gl_entries=gl_entries,
+					account=account,
+					cost_center=item.cost_center,
+					debit=-1 * flt(outgoing_amount, item.precision("base_net_amount")),
+					credit=0.0,
+					remarks=remarks,
+					against_account=stock_asset_account_name,
+					debit_in_account_currency=-1 * flt(outgoing_amount, item.precision("base_net_amount")),
+					account_currency=account_currency,
+					item=item,
+				)
+
+				# check if the exchange rate has changed
+				if d.get("purchase_invoice"):
+					if (
+						exchange_rate_map[item.purchase_invoice]
+						and self.conversion_rate != exchange_rate_map[item.purchase_invoice]
+						and item.net_rate == net_rate_map[item.purchase_invoice_item]
+					):
+
+						discrepancy_caused_by_exchange_rate_difference = (item.qty * item.net_rate) * (
+							exchange_rate_map[item.purchase_invoice] - self.conversion_rate
+						)
+
+						self.add_gl_entry(
+							gl_entries=gl_entries,
+							account=account,
+							cost_center=item.cost_center,
+							debit=0.0,
+							credit=discrepancy_caused_by_exchange_rate_difference,
+							remarks=remarks,
+							against_account=self.supplier,
+							debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
+							account_currency=account_currency,
+							item=item,
+						)
+
+						self.add_gl_entry(
+							gl_entries=gl_entries,
+							account=self.get_company_default("exchange_gain_loss_account"),
+							cost_center=d.cost_center,
+							debit=discrepancy_caused_by_exchange_rate_difference,
+							credit=0.0,
+							remarks=remarks,
+							against_account=self.supplier,
+							debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
+							account_currency=account_currency,
+							item=item,
+						)
+
+			return outgoing_amount
+
+		def make_landed_cost_gl_entries(item):
+			# Amount added through landed-cost-voucher
+			if item.landed_cost_voucher_amount and landed_cost_entries:
+				if (item.item_code, item.name) in landed_cost_entries:
+					for account, amount in landed_cost_entries[(item.item_code, item.name)].items():
+						account_currency = get_account_currency(account)
+						credit_amount = (
+							flt(amount["base_amount"])
+							if (amount["base_amount"] or account_currency != self.company_currency)
+							else flt(amount["amount"])
+						)
+
+						if not account:
+							validate_account("Landed Cost Account")
+
+						self.add_gl_entry(
+							gl_entries=gl_entries,
+							account=account,
+							cost_center=item.cost_center,
+							debit=0.0,
+							credit=credit_amount,
+							remarks=remarks,
+							against_account=stock_asset_account_name,
+							credit_in_account_currency=flt(amount["amount"]),
+							account_currency=account_currency,
+							project=item.project,
+							item=item,
+						)
+
+		def make_rate_difference_entry(item):
+			if item.rate_difference_with_purchase_invoice and stock_asset_rbnb:
+				account_currency = get_account_currency(stock_asset_rbnb)
+				self.add_gl_entry(
+					gl_entries=gl_entries,
+					account=stock_asset_rbnb,
+					cost_center=item.cost_center,
+					debit=0.0,
+					credit=flt(item.rate_difference_with_purchase_invoice),
+					remarks=_("Adjustment based on Purchase Invoice rate"),
+					against_account=stock_asset_account_name,
+					account_currency=account_currency,
+					project=item.project,
+					item=item,
+				)
+
+		def make_sub_contracting_gl_entries(item):
+			# sub-contracting warehouse
+			if flt(item.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse):
+				self.add_gl_entry(
+					gl_entries=gl_entries,
+					account=supplier_warehouse_account,
+					cost_center=item.cost_center,
+					debit=0.0,
+					credit=flt(item.rm_supp_cost),
+					remarks=remarks,
+					against_account=stock_asset_account_name,
+					account_currency=supplier_warehouse_account_currency,
+					item=item,
+				)
+
+		def make_divisional_loss_gl_entry(item, outgoing_amount):
+			if item.is_fixed_asset:
+				return
+
+			# divisional loss adjustment
+			valuation_amount_as_per_doc = (
+				flt(outgoing_amount, d.precision("base_net_amount"))
+				+ flt(item.landed_cost_voucher_amount)
+				+ flt(item.rm_supp_cost)
+				+ flt(item.item_tax_amount)
+				+ flt(item.rate_difference_with_purchase_invoice)
+			)
+
+			divisional_loss = flt(
+				valuation_amount_as_per_doc - flt(stock_value_diff), item.precision("base_net_amount")
+			)
+
+			if divisional_loss:
+				loss_account = (
+					self.get_company_default("default_expense_account", ignore_validation=True)
+					or stock_asset_rbnb
+				)
+
+				cost_center = item.cost_center or frappe.get_cached_value(
+					"Company", self.company, "cost_center"
+				)
+				account_currency = get_account_currency(loss_account)
+				self.add_gl_entry(
+					gl_entries=gl_entries,
+					account=loss_account,
+					cost_center=cost_center,
+					debit=divisional_loss,
+					credit=0.0,
+					remarks=remarks,
+					against_account=stock_asset_account_name,
+					account_currency=account_currency,
+					project=item.project,
+					item=item,
+				)
+
+		stock_items = self.get_stock_items()
+		warehouse_with_no_account = []
+
 		for d in self.get("items"):
-			if d.item_code in stock_items and flt(d.valuation_rate) and flt(d.qty):
-				if warehouse_account.get(d.warehouse):
-					stock_value_diff = frappe.db.get_value(
-						"Stock Ledger Entry",
-						{
-							"voucher_type": "Purchase Receipt",
-							"voucher_no": self.name,
-							"voucher_detail_no": d.name,
-							"warehouse": d.warehouse,
-							"is_cancelled": 0,
-						},
-						"stock_value_difference",
+			if (
+				provisional_accounting_for_non_stock_items
+				and d.item_code not in stock_items
+				and flt(d.qty)
+				and d.get("provisional_expense_account")
+				and not d.is_fixed_asset
+			):
+				self.add_provisional_gl_entry(
+					d, gl_entries, self.posting_date, d.get("provisional_expense_account")
+				)
+			elif flt(d.qty) and (flt(d.valuation_rate) or self.is_return):
+				remarks = self.get("remarks") or _("Accounting Entry for {0}").format(
+					"Asset" if d.is_fixed_asset else "Stock"
+				)
+
+				if not (
+					(erpnext.is_perpetual_inventory_enabled(self.company) and d.item_code in stock_items)
+					or d.is_fixed_asset
+				):
+					continue
+
+				stock_asset_rbnb = (
+					self.get_company_default("asset_received_but_not_billed")
+					if d.is_fixed_asset
+					else self.get_company_default("stock_received_but_not_billed")
+				)
+				landed_cost_entries = get_item_account_wise_additional_cost(self.name)
+
+				if d.is_fixed_asset:
+					account_type = (
+						"capital_work_in_progress_account"
+						if is_cwip_accounting_enabled(d.asset_category)
+						else "fixed_asset_account"
 					)
 
-					warehouse_account_name = warehouse_account[d.warehouse]["account"]
-					warehouse_account_currency = warehouse_account[d.warehouse]["account_currency"]
+					stock_asset_account_name = get_asset_account(
+						account_type, asset_category=d.asset_category, company=self.company
+					)
+
+					stock_value_diff = (
+						flt(d.net_amount)
+						+ flt(d.item_tax_amount / self.conversion_rate)
+						+ flt(d.landed_cost_voucher_amount)
+					)
+				elif warehouse_account.get(d.warehouse):
+					stock_value_diff = get_stock_value_difference(self.name, d.name, d.warehouse)
+					stock_asset_account_name = warehouse_account[d.warehouse]["account"]
 					supplier_warehouse_account = warehouse_account.get(self.supplier_warehouse, {}).get("account")
 					supplier_warehouse_account_currency = warehouse_account.get(self.supplier_warehouse, {}).get(
 						"account_currency"
 					)
-					remarks = self.get("remarks") or _("Accounting Entry for Stock")
 
 					# If PR is sub-contracted and fg item rate is zero
 					# in that case if account for source and target warehouse are same,
@@ -367,214 +589,25 @@
 					if (
 						flt(stock_value_diff) == flt(d.rm_supp_cost)
 						and warehouse_account.get(self.supplier_warehouse)
-						and warehouse_account_name == supplier_warehouse_account
+						and stock_asset_account_name == supplier_warehouse_account
 					):
 						continue
 
-					self.add_gl_entry(
-						gl_entries=gl_entries,
-						account=warehouse_account_name,
-						cost_center=d.cost_center,
-						debit=stock_value_diff,
-						credit=0.0,
-						remarks=remarks,
-						against_account=stock_rbnb,
-						account_currency=warehouse_account_currency,
-						item=d,
-					)
-
-					# GL Entry for from warehouse or Stock Received but not billed
-					# Intentionally passed negative debit amount to avoid incorrect GL Entry validation
-					credit_currency = (
-						get_account_currency(warehouse_account[d.from_warehouse]["account"])
-						if d.from_warehouse
-						else get_account_currency(stock_rbnb)
-					)
-
-					credit_amount = (
-						flt(d.base_net_amount, d.precision("base_net_amount"))
-						if credit_currency == self.company_currency
-						else flt(d.net_amount, d.precision("net_amount"))
-					)
-
-					outgoing_amount = d.base_net_amount
-					if self.is_internal_transfer() and d.valuation_rate:
-						outgoing_amount = abs(
-							frappe.db.get_value(
-								"Stock Ledger Entry",
-								{
-									"voucher_type": "Purchase Receipt",
-									"voucher_no": self.name,
-									"voucher_detail_no": d.name,
-									"warehouse": d.from_warehouse,
-									"is_cancelled": 0,
-								},
-								"stock_value_difference",
-							)
-						)
-						credit_amount = outgoing_amount
-
-					if credit_amount:
-						account = warehouse_account[d.from_warehouse]["account"] if d.from_warehouse else stock_rbnb
-
-						self.add_gl_entry(
-							gl_entries=gl_entries,
-							account=account,
-							cost_center=d.cost_center,
-							debit=-1 * flt(outgoing_amount, d.precision("base_net_amount")),
-							credit=0.0,
-							remarks=remarks,
-							against_account=warehouse_account_name,
-							debit_in_account_currency=-1 * credit_amount,
-							account_currency=credit_currency,
-							item=d,
-						)
-
-						# check if the exchange rate has changed
-						if d.get("purchase_invoice"):
-							if (
-								exchange_rate_map[d.purchase_invoice]
-								and self.conversion_rate != exchange_rate_map[d.purchase_invoice]
-								and d.net_rate == net_rate_map[d.purchase_invoice_item]
-							):
-
-								discrepancy_caused_by_exchange_rate_difference = (d.qty * d.net_rate) * (
-									exchange_rate_map[d.purchase_invoice] - self.conversion_rate
-								)
-
-								self.add_gl_entry(
-									gl_entries=gl_entries,
-									account=account,
-									cost_center=d.cost_center,
-									debit=0.0,
-									credit=discrepancy_caused_by_exchange_rate_difference,
-									remarks=remarks,
-									against_account=self.supplier,
-									debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
-									account_currency=credit_currency,
-									item=d,
-								)
-
-								self.add_gl_entry(
-									gl_entries=gl_entries,
-									account=self.get_company_default("exchange_gain_loss_account"),
-									cost_center=d.cost_center,
-									debit=discrepancy_caused_by_exchange_rate_difference,
-									credit=0.0,
-									remarks=remarks,
-									against_account=self.supplier,
-									debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
-									account_currency=credit_currency,
-									item=d,
-								)
-
-					# Amount added through landed-cos-voucher
-					if d.landed_cost_voucher_amount and landed_cost_entries:
-						if (d.item_code, d.name) in landed_cost_entries:
-							for account, amount in landed_cost_entries[(d.item_code, d.name)].items():
-								account_currency = get_account_currency(account)
-								credit_amount = (
-									flt(amount["base_amount"])
-									if (amount["base_amount"] or account_currency != self.company_currency)
-									else flt(amount["amount"])
-								)
-
-								self.add_gl_entry(
-									gl_entries=gl_entries,
-									account=account,
-									cost_center=d.cost_center,
-									debit=0.0,
-									credit=credit_amount,
-									remarks=remarks,
-									against_account=warehouse_account_name,
-									credit_in_account_currency=flt(amount["amount"]),
-									account_currency=account_currency,
-									project=d.project,
-									item=d,
-								)
-
-					if d.rate_difference_with_purchase_invoice and stock_rbnb:
-						account_currency = get_account_currency(stock_rbnb)
-						self.add_gl_entry(
-							gl_entries=gl_entries,
-							account=stock_rbnb,
-							cost_center=d.cost_center,
-							debit=0.0,
-							credit=flt(d.rate_difference_with_purchase_invoice),
-							remarks=_("Adjustment based on Purchase Invoice rate"),
-							against_account=warehouse_account_name,
-							account_currency=account_currency,
-							project=d.project,
-							item=d,
-						)
-
-					# sub-contracting warehouse
-					if flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse):
-						self.add_gl_entry(
-							gl_entries=gl_entries,
-							account=supplier_warehouse_account,
-							cost_center=d.cost_center,
-							debit=0.0,
-							credit=flt(d.rm_supp_cost),
-							remarks=remarks,
-							against_account=warehouse_account_name,
-							account_currency=supplier_warehouse_account_currency,
-							item=d,
-						)
-
-					# divisional loss adjustment
-					valuation_amount_as_per_doc = (
-						flt(outgoing_amount, d.precision("base_net_amount"))
-						+ flt(d.landed_cost_voucher_amount)
-						+ flt(d.rm_supp_cost)
-						+ flt(d.item_tax_amount)
-						+ flt(d.rate_difference_with_purchase_invoice)
-					)
-
-					divisional_loss = flt(
-						valuation_amount_as_per_doc - flt(stock_value_diff), d.precision("base_net_amount")
-					)
-
-					if divisional_loss:
-						if self.is_return or flt(d.item_tax_amount):
-							loss_account = expenses_included_in_valuation
-						else:
-							loss_account = (
-								self.get_company_default("default_expense_account", ignore_validation=True) or stock_rbnb
-							)
-
-						cost_center = d.cost_center or frappe.get_cached_value(
-							"Company", self.company, "cost_center"
-						)
-
-						self.add_gl_entry(
-							gl_entries=gl_entries,
-							account=loss_account,
-							cost_center=cost_center,
-							debit=divisional_loss,
-							credit=0.0,
-							remarks=remarks,
-							against_account=warehouse_account_name,
-							account_currency=credit_currency,
-							project=d.project,
-							item=d,
-						)
-
-				elif (
-					d.warehouse not in warehouse_with_no_account
-					or d.rejected_warehouse not in warehouse_with_no_account
-				):
-					warehouse_with_no_account.append(d.warehouse)
+				if (flt(d.valuation_rate) or self.is_return or d.is_fixed_asset) and flt(d.qty):
+					make_item_asset_inward_gl_entry(d, stock_value_diff, stock_asset_account_name)
+					outgoing_amount = make_stock_received_but_not_billed_entry(d)
+					make_landed_cost_gl_entries(d)
+					make_rate_difference_entry(d)
+					make_sub_contracting_gl_entries(d)
+					make_divisional_loss_gl_entry(d, outgoing_amount)
 			elif (
-				d.item_code not in stock_items
-				and not d.is_fixed_asset
-				and flt(d.qty)
-				and provisional_accounting_for_non_stock_items
-				and d.get("provisional_expense_account")
+				d.warehouse not in warehouse_with_no_account
+				or d.rejected_warehouse not in warehouse_with_no_account
 			):
-				self.add_provisional_gl_entry(
-					d, gl_entries, self.posting_date, d.get("provisional_expense_account")
-				)
+				warehouse_with_no_account.append(d.warehouse)
+
+			if d.is_fixed_asset:
+				self.update_assets(d, d.valuation_rate)
 
 		if warehouse_with_no_account:
 			frappe.msgprint(
@@ -587,8 +620,8 @@
 		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
+		debit_currency = get_account_currency(item.expense_account)
 		remarks = self.get("remarks") or _("Accounting Entry for Service")
 		multiplication_factor = 1
 
@@ -629,11 +662,8 @@
 		)
 
 	def make_tax_gl_entries(self, gl_entries):
-
-		if erpnext.is_perpetual_inventory_enabled(self.company):
-			expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
-
 		negative_expense_to_be_booked = sum([flt(d.item_tax_amount) for d in self.get("items")])
+		is_asset_pr = any(d.is_fixed_asset for d in self.get("items"))
 		# Cost center-wise amount breakup for other charges included for valuation
 		valuation_tax = {}
 		for tax in self.get("taxes"):
@@ -653,26 +683,26 @@
 
 		if negative_expense_to_be_booked and valuation_tax:
 			# Backward compatibility:
-			# If expenses_included_in_valuation account has been credited in against PI
 			# and charges added via Landed Cost Voucher,
 			# post valuation related charges on "Stock Received But Not Billed"
-			# introduced in 2014 for backward compatibility of expenses already booked in expenses_included_in_valuation account
-
-			negative_expense_booked_in_pi = frappe.db.sql(
-				"""select name from `tabPurchase Invoice Item` pi
-				where docstatus = 1 and purchase_receipt=%s
-				and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice'
-					and voucher_no=pi.parent and account=%s)""",
-				(self.name, expenses_included_in_valuation),
-			)
-
 			against_account = ", ".join([d.account for d in gl_entries if flt(d.debit) > 0])
 			total_valuation_amount = sum(valuation_tax.values())
 			amount_including_divisional_loss = negative_expense_to_be_booked
-			stock_rbnb = self.get_company_default("stock_received_but_not_billed")
+			stock_rbnb = (
+				self.get("asset_received_but_not_billed")
+				if is_asset_pr
+				else self.get_company_default("stock_received_but_not_billed")
+			)
 			i = 1
 			for tax in self.get("taxes"):
 				if valuation_tax.get(tax.name):
+					negative_expense_booked_in_pi = frappe.db.sql(
+						"""select name from `tabPurchase Invoice Item` pi
+						where docstatus = 1 and purchase_receipt=%s
+						and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice'
+							and voucher_no=pi.parent and account=%s)""",
+						(self.name, tax.account_head),
+					)
 
 					if negative_expense_booked_in_pi:
 						account = stock_rbnb
@@ -700,103 +730,6 @@
 
 					i += 1
 
-	def get_asset_gl_entry(self, gl_entries):
-		for item in self.get("items"):
-			if item.is_fixed_asset:
-				if is_cwip_accounting_enabled(item.asset_category):
-					self.add_asset_gl_entries(item, gl_entries)
-				if flt(item.landed_cost_voucher_amount):
-					self.add_lcv_gl_entries(item, gl_entries)
-					# update assets gross amount by its valuation rate
-					# valuation rate is total of net rate, raw mat supp cost, tax amount, lcv amount per item
-					self.update_assets(item, item.valuation_rate)
-		return gl_entries
-
-	def add_asset_gl_entries(self, item, gl_entries):
-		arbnb_account = self.get_company_default("asset_received_but_not_billed")
-		# This returns category's cwip account if not then fallback to company's default cwip account
-		cwip_account = get_asset_account(
-			"capital_work_in_progress_account", asset_category=item.asset_category, company=self.company
-		)
-
-		asset_amount = flt(item.net_amount) + flt(item.item_tax_amount / self.conversion_rate)
-		base_asset_amount = flt(item.base_net_amount + item.item_tax_amount)
-		remarks = self.get("remarks") or _("Accounting Entry for Asset")
-
-		cwip_account_currency = get_account_currency(cwip_account)
-		# debit cwip account
-		debit_in_account_currency = (
-			base_asset_amount if cwip_account_currency == self.company_currency else asset_amount
-		)
-
-		self.add_gl_entry(
-			gl_entries=gl_entries,
-			account=cwip_account,
-			cost_center=item.cost_center,
-			debit=base_asset_amount,
-			credit=0.0,
-			remarks=remarks,
-			against_account=arbnb_account,
-			debit_in_account_currency=debit_in_account_currency,
-			item=item,
-		)
-
-		asset_rbnb_currency = get_account_currency(arbnb_account)
-		# credit arbnb account
-		credit_in_account_currency = (
-			base_asset_amount if asset_rbnb_currency == self.company_currency else asset_amount
-		)
-
-		self.add_gl_entry(
-			gl_entries=gl_entries,
-			account=arbnb_account,
-			cost_center=item.cost_center,
-			debit=0.0,
-			credit=base_asset_amount,
-			remarks=remarks,
-			against_account=cwip_account,
-			credit_in_account_currency=credit_in_account_currency,
-			item=item,
-		)
-
-	def add_lcv_gl_entries(self, item, gl_entries):
-		expenses_included_in_asset_valuation = self.get_company_default(
-			"expenses_included_in_asset_valuation"
-		)
-		if not is_cwip_accounting_enabled(item.asset_category):
-			asset_account = get_asset_category_account(
-				asset_category=item.asset_category, fieldname="fixed_asset_account", company=self.company
-			)
-		else:
-			# This returns company's default cwip account
-			asset_account = get_asset_account("capital_work_in_progress_account", company=self.company)
-
-		remarks = self.get("remarks") or _("Accounting Entry for Stock")
-
-		self.add_gl_entry(
-			gl_entries=gl_entries,
-			account=expenses_included_in_asset_valuation,
-			cost_center=item.cost_center,
-			debit=0.0,
-			credit=flt(item.landed_cost_voucher_amount),
-			remarks=remarks,
-			against_account=asset_account,
-			project=item.project,
-			item=item,
-		)
-
-		self.add_gl_entry(
-			gl_entries=gl_entries,
-			account=asset_account,
-			cost_center=item.cost_center,
-			debit=flt(item.landed_cost_voucher_amount),
-			credit=0.0,
-			remarks=remarks,
-			against_account=expenses_included_in_asset_valuation,
-			project=item.project,
-			item=item,
-		)
-
 	def update_assets(self, item, valuation_rate):
 		assets = frappe.db.get_all(
 			"Asset", filters={"purchase_receipt": self.name, "item_code": item.item_code}
@@ -821,16 +754,59 @@
 				po_details.append(d.purchase_order_item)
 
 		if po_details:
-			updated_pr += update_billed_amount_based_on_po(po_details, update_modified)
+			updated_pr += update_billed_amount_based_on_po(po_details, update_modified, self)
 
 		for pr in set(updated_pr):
 			pr_doc = self if (pr == self.name) else frappe.get_doc("Purchase Receipt", pr)
 			update_billing_percentage(pr_doc, update_modified=update_modified)
 
-		self.load_from_db()
+	def reserve_stock_for_sales_order(self):
+		if self.is_return or not cint(
+			frappe.db.get_single_value("Stock Settings", "auto_reserve_stock_for_sales_order_on_purchase")
+		):
+			return
+
+		self.reload()  # reload to get the Serial and Batch Bundle Details
+
+		so_items_details_map = {}
+		for item in self.items:
+			if item.sales_order and item.sales_order_item:
+				item_details = {
+					"name": item.sales_order_item,
+					"item_code": item.item_code,
+					"warehouse": item.warehouse,
+					"qty_to_reserve": item.stock_qty,
+					"from_voucher_no": item.parent,
+					"from_voucher_detail_no": item.name,
+					"serial_and_batch_bundle": item.serial_and_batch_bundle,
+				}
+				so_items_details_map.setdefault(item.sales_order, []).append(item_details)
+
+		if so_items_details_map:
+			for so, items_details in so_items_details_map.items():
+				so_doc = frappe.get_doc("Sales Order", so)
+				so_doc.create_stock_reservation_entries(
+					items_details=items_details,
+					from_voucher_type="Purchase Receipt",
+					notify=True,
+				)
 
 
-def update_billed_amount_based_on_po(po_details, update_modified=True):
+def get_stock_value_difference(voucher_no, voucher_detail_no, warehouse):
+	return frappe.db.get_value(
+		"Stock Ledger Entry",
+		{
+			"voucher_type": "Purchase Receipt",
+			"voucher_no": voucher_no,
+			"voucher_detail_no": voucher_detail_no,
+			"warehouse": warehouse,
+			"is_cancelled": 0,
+		},
+		"stock_value_difference",
+	)
+
+
+def update_billed_amount_based_on_po(po_details, update_modified=True, pr_doc=None):
 	po_billed_amt_details = get_billed_amount_against_po(po_details)
 
 	# Get all Purchase Receipt Item rows against the Purchase Order Items
@@ -859,13 +835,19 @@
 		po_billed_amt_details[pr_item.purchase_order_item] = billed_against_po
 
 		if pr_item.billed_amt != billed_amt_agianst_pr:
-			frappe.db.set_value(
-				"Purchase Receipt Item",
-				pr_item.name,
-				"billed_amt",
-				billed_amt_agianst_pr,
-				update_modified=update_modified,
-			)
+			# update existing doc if possible
+			if pr_doc and pr_item.parent == pr_doc.name:
+				pr_item = next((item for item in pr_doc.items if item.name == pr_item.name), None)
+				pr_item.db_set("billed_amt", billed_amt_agianst_pr, update_modified=update_modified)
+
+			else:
+				frappe.db.set_value(
+					"Purchase Receipt Item",
+					pr_item.name,
+					"billed_amt",
+					billed_amt_agianst_pr,
+					update_modified=update_modified,
+				)
 
 			updated_pr.append(pr_item.parent)
 
@@ -941,9 +923,6 @@
 
 
 def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate=False):
-	# Reload as billed amount was set in db directly
-	pr_doc.load_from_db()
-
 	# Update Billing % based on pending accepted qty
 	total_amount, total_billed_amount = 0, 0
 	item_wise_returned_qty = get_item_wise_returned_qty(pr_doc)
@@ -969,7 +948,6 @@
 
 	percent_billed = round(100 * (total_billed_amount / (total_amount or 1)), 6)
 	pr_doc.db_set("per_billed", percent_billed)
-	pr_doc.load_from_db()
 
 	if update_modified:
 		pr_doc.set_status(update=True)
@@ -1091,6 +1069,7 @@
 					"is_fixed_asset": "is_fixed_asset",
 					"asset_location": "asset_location",
 					"asset_category": "asset_category",
+					"wip_composite_asset": "wip_composite_asset",
 				},
 				"postprocess": update_item,
 				"filter": lambda d: get_pending_qty(d)[0] <= 0
@@ -1255,3 +1234,8 @@
 
 def on_doctype_update():
 	frappe.db.add_index("Purchase Receipt", ["supplier", "is_return", "return_against"])
+
+
+@erpnext.allow_regional
+def update_regional_gl_entries(gl_list, doc):
+	return
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py
index b3ae7b5..71489fb 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py
@@ -10,6 +10,7 @@
 			"Landed Cost Voucher": "receipt_document",
 			"Auto Repeat": "reference_document",
 			"Purchase Receipt": "return_against",
+			"Stock Reservation Entry": "from_voucher_no",
 		},
 		"internal_links": {
 			"Material Request": ["items", "material_request"],
@@ -18,7 +19,10 @@
 			"Quality Inspection": ["items", "quality_inspection"],
 		},
 		"transactions": [
-			{"label": _("Related"), "items": ["Purchase Invoice", "Landed Cost Voucher", "Asset"]},
+			{
+				"label": _("Related"),
+				"items": ["Purchase Invoice", "Landed Cost Voucher", "Asset", "Stock Reservation Entry"],
+			},
 			{
 				"label": _("Reference"),
 				"items": ["Material Request", "Purchase Order", "Quality Inspection", "Project"],
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index a8ef5e8..146cbff 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -958,17 +958,33 @@
 		pr1.cancel()
 
 	def test_stock_transfer_from_purchase_receipt(self):
+		from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt
+		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+
+		prepare_data_for_internal_transfer()
+
+		customer = "_Test Internal Customer 2"
+		company = "_Test Company with perpetual inventory"
+
 		pr1 = make_purchase_receipt(
-			warehouse="Work In Progress - TCP1", company="_Test Company with perpetual inventory"
+			warehouse="Stores - TCP1", company="_Test Company with perpetual inventory"
 		)
 
-		pr = make_purchase_receipt(
-			company="_Test Company with perpetual inventory", warehouse="Stores - TCP1", do_not_save=1
+		dn1 = create_delivery_note(
+			item_code=pr1.items[0].item_code,
+			company=company,
+			customer=customer,
+			cost_center="Main - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			qty=5,
+			rate=500,
+			warehouse="Stores - TCP1",
+			target_warehouse="Work In Progress - TCP1",
 		)
 
-		pr.supplier_warehouse = ""
+		pr = make_inter_company_purchase_receipt(dn1.name)
 		pr.items[0].from_warehouse = "Work In Progress - TCP1"
-
+		pr.items[0].warehouse = "Stores - TCP1"
 		pr.submit()
 
 		gl_entries = get_gl_entries("Purchase Receipt", pr.name)
@@ -982,9 +998,13 @@
 			self.assertEqual(expected_sle[sle.warehouse], sle.actual_qty)
 
 		pr.cancel()
-		pr1.cancel()
 
 	def test_stock_transfer_from_purchase_receipt_with_valuation(self):
+		from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt
+		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+
+		prepare_data_for_internal_transfer()
+
 		create_warehouse(
 			"_Test Warehouse for Valuation",
 			company="_Test Company with perpetual inventory",
@@ -992,16 +1012,28 @@
 		)
 
 		pr1 = make_purchase_receipt(
-			warehouse="_Test Warehouse for Valuation - TCP1",
+			warehouse="Stores - TCP1",
 			company="_Test Company with perpetual inventory",
 		)
 
-		pr = make_purchase_receipt(
-			company="_Test Company with perpetual inventory", warehouse="Stores - TCP1", do_not_save=1
+		customer = "_Test Internal Customer 2"
+		company = "_Test Company with perpetual inventory"
+
+		dn1 = create_delivery_note(
+			item_code=pr1.items[0].item_code,
+			company=company,
+			customer=customer,
+			cost_center="Main - TCP1",
+			expense_account="Cost of Goods Sold - TCP1",
+			qty=5,
+			rate=50,
+			warehouse="Stores - TCP1",
+			target_warehouse="_Test Warehouse for Valuation - TCP1",
 		)
 
+		pr = make_inter_company_purchase_receipt(dn1.name)
 		pr.items[0].from_warehouse = "_Test Warehouse for Valuation - TCP1"
-		pr.supplier_warehouse = ""
+		pr.items[0].warehouse = "Stores - TCP1"
 
 		pr.append(
 			"taxes",
@@ -1037,7 +1069,6 @@
 			self.assertEqual(gle.credit, expected_gle[i][2])
 
 		pr.cancel()
-		pr1.cancel()
 
 	def test_po_to_pi_and_po_to_pr_worflow_full(self):
 		"""Test following behaviour:
@@ -2086,6 +2117,77 @@
 		return_pr.reload()
 		self.assertEqual(return_pr.status, "Completed")
 
+	def test_purchase_return_with_zero_rate(self):
+		company = "_Test Company with perpetual inventory"
+
+		# Step - 1: Create Item
+		item, warehouse = (
+			make_item(properties={"is_stock_item": 1, "valuation_method": "Moving Average"}).name,
+			"Stores - TCP1",
+		)
+
+		# Step - 2: Create Stock Entry (Material Receipt)
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+
+		se = make_stock_entry(
+			purpose="Material Receipt",
+			item_code=item,
+			qty=100,
+			basic_rate=100,
+			to_warehouse=warehouse,
+			company=company,
+		)
+
+		# Step - 3: Create Purchase Receipt
+		pr = make_purchase_receipt(
+			item_code=item,
+			qty=5,
+			rate=0,
+			warehouse=warehouse,
+			company=company,
+		)
+
+		# Step - 4: Create Purchase Return
+		from erpnext.controllers.sales_and_purchase_return import make_return_doc
+
+		pr_return = make_return_doc("Purchase Receipt", pr.name)
+		pr_return.save()
+		pr_return.submit()
+
+		sl_entries = get_sl_entries(pr_return.doctype, pr_return.name)
+		gl_entries = get_gl_entries(pr_return.doctype, pr_return.name)
+
+		# Test - 1: SLE Stock Value Difference should be equal to Qty * Average Rate
+		average_rate = (
+			(se.items[0].qty * se.items[0].basic_rate) + (pr.items[0].qty * pr.items[0].rate)
+		) / (se.items[0].qty + pr.items[0].qty)
+		expected_stock_value_difference = pr_return.items[0].qty * average_rate
+		self.assertEqual(
+			flt(sl_entries[0].stock_value_difference, 2), flt(expected_stock_value_difference, 2)
+		)
+
+		# Test - 2: GL Entries should be created for Stock Value Difference
+		self.assertEqual(len(gl_entries), 2)
+
+		# Test - 3: SLE Stock Value Difference should be equal to Debit or Credit of GL Entries.
+		for entry in gl_entries:
+			self.assertEqual(abs(entry.debit + entry.credit), abs(sl_entries[0].stock_value_difference))
+
+	def non_internal_transfer_purchase_receipt(self):
+		from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+
+		pr_doc = make_purchase_receipt(do_not_submit=True)
+		warehouse = create_warehouse("Internal Transfer Warehouse", pr_doc.company)
+		pr_doc.items[0].db_set("target_warehouse", "warehouse")
+
+		pr_doc.reload()
+
+		self.assertEqual(pr_doc.items[0].from_warehouse, warehouse.name)
+
+		pr_doc.save()
+		pr_doc.reload()
+		self.assertFalse(pr_doc.items[0].from_warehouse)
+
 
 def prepare_data_for_internal_transfer():
 	from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier
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 d93d21c..f5240a6 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -125,7 +125,9 @@
   "dimension_col_break",
   "cost_center",
   "section_break_80",
-  "page_break"
+  "page_break",
+  "sales_order",
+  "sales_order_item"
  ],
  "fields": [
   {
@@ -1062,12 +1064,32 @@
    "fieldtype": "Link",
    "label": "WIP Composite Asset",
    "options": "Asset"
+  },
+  {
+   "fieldname": "sales_order",
+   "fieldtype": "Link",
+   "label": "Sales Order",
+   "no_copy": 1,
+   "options": "Sales Order",
+   "print_hide": 1,
+   "read_only": 1,
+   "search_index": 1
+  },
+  {
+   "fieldname": "sales_order_item",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Sales Order Item",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1,
+   "search_index": 1
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-10-03 21:11:50.547261",
+ "modified": "2023-10-19 10:50:58.071735",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt Item",
@@ -1078,4 +1100,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "states": []
-}
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
index 96e4a55..f96c184 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
@@ -658,7 +658,7 @@
 		if not available_batches:
 			return
 
-		available_batches = get_availabel_batches_qty(available_batches)
+		available_batches = get_available_batches_qty(available_batches)
 		for batch_no in batches:
 			if batch_no not in available_batches or available_batches[batch_no] < 0:
 				self.throw_error_message(
@@ -1074,7 +1074,7 @@
 		return get_auto_batch_nos(kwargs)
 
 
-def get_availabel_batches_qty(available_batches):
+def get_available_batches_qty(available_batches):
 	available_batches_qty = defaultdict(float)
 	for batch in available_batches:
 		available_batches_qty[batch.batch_no] += batch.qty
@@ -1301,6 +1301,7 @@
 		"POS Invoice",
 		fields=[
 			"`tabPOS Invoice Item`.batch_no",
+			"`tabPOS Invoice Item`.qty",
 			"`tabPOS Invoice`.is_return",
 			"`tabPOS Invoice Item`.warehouse",
 			"`tabPOS Invoice Item`.name as child_docname",
@@ -1321,9 +1322,6 @@
 		if pos_invoice.serial_and_batch_bundle
 	]
 
-	if not ids:
-		return {}
-
 	if ids:
 		for d in get_serial_batch_ledgers(kwargs.item_code, docstatus=1, name=ids):
 			key = (d.batch_no, d.warehouse)
@@ -1337,6 +1335,7 @@
 			else:
 				pos_batches[key].qty += d.qty
 
+	# POS invoices having batch without bundle (to handle old POS invoices)
 	for row in pos_invoices:
 		if not row.batch_no:
 			continue
@@ -1346,11 +1345,11 @@
 
 		key = (row.batch_no, row.warehouse)
 		if key in pos_batches:
-			pos_batches[key] -= row.qty * -1 if row.is_return else row.qty
+			pos_batches[key]["qty"] -= row.qty * -1 if row.is_return else row.qty
 		else:
 			pos_batches[key] = frappe._dict(
 				{
-					"qty": (row.qty * -1 if row.is_return else row.qty),
+					"qty": (row.qty * -1 if not row.is_return else row.qty),
 					"warehouse": row.warehouse,
 				}
 			)
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index a2cae7f..c41349f 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -438,31 +438,37 @@
 								item_code.append(item.item_code)
 
 	def validate_fg_completed_qty(self):
-		item_wise_qty = {}
-		if self.purpose == "Manufacture" and self.work_order:
-			for d in self.items:
-				if d.is_finished_item:
-					if self.process_loss_qty:
-						d.qty = self.fg_completed_qty - self.process_loss_qty
+		if self.purpose != "Manufacture":
+			return
 
-					item_wise_qty.setdefault(d.item_code, []).append(d.qty)
+		fg_qty = defaultdict(float)
+		for d in self.items:
+			if d.is_finished_item:
+				fg_qty[d.item_code] += flt(d.qty)
+
+		if not fg_qty:
+			return
 
 		precision = frappe.get_precision("Stock Entry Detail", "qty")
-		for item_code, qty_list in item_wise_qty.items():
-			total = flt(sum(qty_list), precision)
+		fg_item = list(fg_qty.keys())[0]
+		fg_item_qty = flt(fg_qty[fg_item], precision)
+		fg_completed_qty = flt(self.fg_completed_qty, precision)
 
-			if (self.fg_completed_qty - total) > 0 and not self.process_loss_qty:
-				self.process_loss_qty = flt(self.fg_completed_qty - total, precision)
-				self.process_loss_percentage = flt(self.process_loss_qty * 100 / self.fg_completed_qty)
+		for d in self.items:
+			if not fg_qty.get(d.item_code):
+				continue
 
-			if self.process_loss_qty:
-				total += flt(self.process_loss_qty, precision)
+			if (fg_completed_qty - fg_item_qty) > 0:
+				self.process_loss_qty = fg_completed_qty - fg_item_qty
 
-			if self.fg_completed_qty != total:
+			if not self.process_loss_qty:
+				continue
+
+			if fg_completed_qty != (flt(fg_item_qty) + flt(self.process_loss_qty, precision)):
 				frappe.throw(
-					_("The finished product {0} quantity {1} and For Quantity {2} cannot be different").format(
-						frappe.bold(item_code), frappe.bold(total), frappe.bold(self.fg_completed_qty)
-					)
+					_(
+						"Since there is a process loss of {0} units for the finished good {1}, you should reduce the quantity by {0} units for the finished good {1} in the Items Table."
+					).format(frappe.bold(self.process_loss_qty), frappe.bold(d.item_code))
 				)
 
 	def validate_difference_account(self):
@@ -1014,14 +1020,34 @@
 						& (se.docstatus == 1)
 						& (se_detail.item_code == se_item.item_code)
 						& (
-							(se.purchase_order == self.purchase_order)
+							((se.purchase_order == self.purchase_order) & (se_detail.po_detail == se_item.po_detail))
 							if self.subcontract_data.order_doctype == "Purchase Order"
-							else (se.subcontracting_order == self.subcontracting_order)
+							else (
+								(se.subcontracting_order == self.subcontracting_order)
+								& (se_detail.sco_rm_detail == se_item.sco_rm_detail)
+							)
 						)
 					)
-				).run()[0][0]
+				).run()[0][0] or 0
 
-				if flt(total_supplied, precision) > flt(total_allowed, precision):
+				total_returned = 0
+				if self.subcontract_data.order_doctype == "Subcontracting Order":
+					total_returned = (
+						frappe.qb.from_(se)
+						.inner_join(se_detail)
+						.on(se.name == se_detail.parent)
+						.select(Sum(se_detail.transfer_qty))
+						.where(
+							(se.purpose == "Material Transfer")
+							& (se.docstatus == 1)
+							& (se.is_return == 1)
+							& (se_detail.item_code == se_item.item_code)
+							& (se_detail.sco_rm_detail == se_item.sco_rm_detail)
+							& (se.subcontracting_order == self.subcontracting_order)
+						)
+					).run()[0][0] or 0
+
+				if flt(total_supplied - total_returned, precision) > flt(total_allowed, precision):
 					frappe.throw(
 						_("Row {0}# Item {1} cannot be transferred more than {2} against {3} {4}").format(
 							se_item.idx,
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index cc8a108..3e0610e 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -449,9 +449,7 @@
 		repack.posting_date = nowdate()
 		repack.posting_time = nowtime()
 
-		expenses_included_in_valuation = frappe.get_value(
-			"Company", company, "expenses_included_in_valuation"
-		)
+		default_expense_account = frappe.get_value("Company", company, "default_expense_account")
 
 		items = get_multiple_items()
 		repack.items = []
@@ -462,12 +460,12 @@
 			"additional_costs",
 			[
 				{
-					"expense_account": expenses_included_in_valuation,
+					"expense_account": default_expense_account,
 					"description": "Actual Operating Cost",
 					"amount": 1000,
 				},
 				{
-					"expense_account": expenses_included_in_valuation,
+					"expense_account": default_expense_account,
 					"description": "Additional Operating Cost",
 					"amount": 200,
 				},
@@ -506,9 +504,7 @@
 		self.check_gl_entries(
 			"Stock Entry",
 			repack.name,
-			sorted(
-				[[stock_in_hand_account, 1200, 0.0], ["Expenses Included In Valuation - TCP1", 0.0, 1200.0]]
-			),
+			sorted([[stock_in_hand_account, 1200, 0.0], ["Cost of Goods Sold - TCP1", 0.0, 1200.0]]),
 		)
 
 	def check_stock_ledger_entries(self, voucher_type, voucher_no, expected_sle):
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index 3ca4bad..c1b2051 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -5,14 +5,16 @@
 from datetime import date
 
 import frappe
-from frappe import _
+from frappe import _, bold
 from frappe.core.doctype.role.role import get_users
 from frappe.model.document import Document
-from frappe.utils import add_days, cint, formatdate, get_datetime, getdate
+from frappe.utils import add_days, cint, flt, formatdate, get_datetime, getdate
 
 from erpnext.accounts.utils import get_fiscal_year
 from erpnext.controllers.item_variant import ItemTemplateCannotHaveStock
+from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions
 from erpnext.stock.serial_batch_bundle import SerialBatchBundle
+from erpnext.stock.stock_ledger import get_previous_sle
 
 
 class StockFreezeError(frappe.ValidationError):
@@ -48,6 +50,69 @@
 		self.validate_and_set_fiscal_year()
 		self.block_transactions_against_group_warehouse()
 		self.validate_with_last_transaction_posting_time()
+		self.validate_inventory_dimension_negative_stock()
+
+	def validate_inventory_dimension_negative_stock(self):
+		extra_cond = ""
+		kwargs = {}
+
+		dimensions = self._get_inventory_dimensions()
+		if not dimensions:
+			return
+
+		for dimension, values in dimensions.items():
+			kwargs[dimension] = values.get("value")
+			extra_cond += f" and {dimension} = %({dimension})s"
+
+		kwargs.update(
+			{
+				"item_code": self.item_code,
+				"warehouse": self.warehouse,
+				"posting_date": self.posting_date,
+				"posting_time": self.posting_time,
+				"company": self.company,
+			}
+		)
+
+		sle = get_previous_sle(kwargs, extra_cond=extra_cond)
+		if sle:
+			flt_precision = cint(frappe.db.get_default("float_precision")) or 2
+			diff = sle.qty_after_transaction + flt(self.actual_qty)
+			diff = flt(diff, flt_precision)
+			if diff < 0 and abs(diff) > 0.0001:
+				self.throw_validation_error(diff, dimensions)
+
+	def throw_validation_error(self, diff, dimensions):
+		dimension_msg = _(", with the inventory {0}: {1}").format(
+			"dimensions" if len(dimensions) > 1 else "dimension",
+			", ".join(f"{bold(d.doctype)} ({d.value})" for k, d in dimensions.items()),
+		)
+
+		msg = _(
+			"{0} units of {1} are required in {2}{3}, on {4} {5} for {6} to complete the transaction."
+		).format(
+			abs(diff),
+			frappe.get_desk_link("Item", self.item_code),
+			frappe.get_desk_link("Warehouse", self.warehouse),
+			dimension_msg,
+			self.posting_date,
+			self.posting_time,
+			frappe.get_desk_link(self.voucher_type, self.voucher_no),
+		)
+
+		frappe.throw(msg, title=_("Inventory Dimension Negative Stock"))
+
+	def _get_inventory_dimensions(self):
+		inv_dimensions = get_inventory_dimensions()
+		inv_dimension_dict = {}
+		for dimension in inv_dimensions:
+			if not dimension.get("validate_negative_stock") or not self.get(dimension.fieldname):
+				continue
+
+			dimension["value"] = self.get(dimension.fieldname)
+			inv_dimension_dict.setdefault(dimension.fieldname, dimension)
+
+		return inv_dimension_dict
 
 	def on_submit(self):
 		self.check_stock_frozen_date()
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
index 5452692..b3998b7 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -123,13 +123,6 @@
 				fieldname: "item_code",
 				fieldtype: "Link",
 				options: "Item",
-				"get_query": function() {
-					return {
-						"filters": {
-							"disabled": 0,
-						}
-					};
-				}
 			},
 			{
 				label: __("Ignore Empty Stock"),
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index e36d576..98b4ffd 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -12,6 +12,7 @@
 from erpnext.accounts.utils import get_company_default
 from erpnext.controllers.stock_controller import StockController
 from erpnext.stock.doctype.batch.batch import get_available_batches, get_batch_qty
+from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions
 from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
 	get_available_serial_nos,
 )
@@ -50,6 +51,7 @@
 		self.clean_serial_nos()
 		self.set_total_qty_and_amount()
 		self.validate_putaway_capacity()
+		self.validate_inventory_dimension()
 
 		if self._action == "submit":
 			self.validate_reserved_stock()
@@ -57,6 +59,17 @@
 	def on_update(self):
 		self.set_serial_and_batch_bundle(ignore_validate=True)
 
+	def validate_inventory_dimension(self):
+		dimensions = get_inventory_dimensions()
+		for dimension in dimensions:
+			for row in self.items:
+				if not row.batch_no and row.current_qty and row.get(dimension.get("fieldname")):
+					frappe.throw(
+						_(
+							"Row #{0}: You cannot use the inventory dimension '{1}' in Stock Reconciliation to modify the quantity or valuation rate. Stock reconciliation with inventory dimensions is intended solely for performing opening entries."
+						).format(row.idx, bold(dimension.get("doctype")))
+					)
+
 	def on_submit(self):
 		self.update_stock_ledger()
 		self.make_gl_entries()
@@ -202,8 +215,19 @@
 				self.calculate_difference_amount(item, bundle_data)
 				return True
 
+			inventory_dimensions_dict = {}
+			if not item.batch_no and not item.serial_no:
+				for dimension in get_inventory_dimensions():
+					if item.get(dimension.get("fieldname")):
+						inventory_dimensions_dict[dimension.get("fieldname")] = item.get(dimension.get("fieldname"))
+
 			item_dict = get_stock_balance_for(
-				item.item_code, item.warehouse, self.posting_date, self.posting_time, batch_no=item.batch_no
+				item.item_code,
+				item.warehouse,
+				self.posting_date,
+				self.posting_time,
+				batch_no=item.batch_no,
+				inventory_dimensions_dict=inventory_dimensions_dict,
 			)
 
 			if (item.qty is None or item.qty == item_dict.get("qty")) and (
@@ -507,7 +531,13 @@
 		if not row.batch_no:
 			data.qty_after_transaction = flt(row.qty, row.precision("qty"))
 
-		if self.docstatus == 2:
+		dimensions = get_inventory_dimensions()
+		has_dimensions = False
+		for dimension in dimensions:
+			if row.get(dimension.get("fieldname")):
+				has_dimensions = True
+
+		if self.docstatus == 2 and (not row.batch_no or not row.serial_and_batch_bundle):
 			if row.current_qty:
 				data.actual_qty = -1 * row.current_qty
 				data.qty_after_transaction = flt(row.current_qty)
@@ -523,6 +553,13 @@
 				data.valuation_rate = flt(row.valuation_rate)
 				data.stock_value_difference = -1 * flt(row.amount_difference)
 
+		elif (
+			self.docstatus == 1 and has_dimensions and (not row.batch_no or not row.serial_and_batch_bundle)
+		):
+			data.actual_qty = row.qty
+			data.qty_after_transaction = 0.0
+			data.incoming_rate = flt(row.valuation_rate)
+
 		self.update_inventory_dimensions(row, data)
 
 		return data
@@ -911,6 +948,7 @@
 	posting_time,
 	batch_no: Optional[str] = None,
 	with_valuation_rate: bool = True,
+	inventory_dimensions_dict=None,
 ):
 	frappe.has_permission("Stock Reconciliation", "write", throw=True)
 
@@ -939,6 +977,7 @@
 		posting_time,
 		with_valuation_rate=with_valuation_rate,
 		with_serial_no=has_serial_no,
+		inventory_dimensions_dict=inventory_dimensions_dict,
 	)
 
 	if has_serial_no:
diff --git a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.js b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.js
index c5df319..f60a037 100644
--- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.js
+++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.js
@@ -92,7 +92,7 @@
 			'qty', 'read_only', frm.doc.has_serial_no
 		);
 
-		frm.set_df_property('sb_entries', 'allow_on_submit', frm.doc.against_pick_list ? 0 : 1);
+		frm.set_df_property('sb_entries', 'allow_on_submit', frm.doc.from_voucher_type == "Pick List" ? 0 : 1);
 	},
 
 	hide_rate_related_fields(frm) {
diff --git a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json
index 5c3018f..76cedd4 100644
--- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json
+++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json
@@ -17,8 +17,9 @@
   "voucher_no",
   "voucher_detail_no",
   "column_break_7dxj",
-  "against_pick_list",
-  "against_pick_list_item",
+  "from_voucher_type",
+  "from_voucher_no",
+  "from_voucher_detail_no",
   "section_break_xt4m",
   "stock_uom",
   "column_break_grdt",
@@ -158,7 +159,7 @@
    "oldfieldname": "actual_qty",
    "oldfieldtype": "Currency",
    "print_width": "150px",
-   "read_only_depends_on": "eval: ((doc.reservation_based_on == \"Serial and Batch\") || (doc.against_pick_list) || (doc.delivered_qty > 0))",
+   "read_only_depends_on": "eval: ((doc.reservation_based_on == \"Serial and Batch\") || (doc.from_voucher_type == \"Pick List\") || (doc.delivered_qty > 0))",
    "width": "150px"
   },
   {
@@ -268,27 +269,7 @@
    "label": "Reservation Based On",
    "no_copy": 1,
    "options": "Qty\nSerial and Batch",
-   "read_only_depends_on": "eval: (doc.delivered_qty > 0 || doc.against_pick_list)"
-  },
-  {
-   "fieldname": "against_pick_list",
-   "fieldtype": "Link",
-   "label": "Against Pick List",
-   "no_copy": 1,
-   "options": "Pick List",
-   "print_hide": 1,
-   "read_only": 1,
-   "report_hide": 1,
-   "search_index": 1
-  },
-  {
-   "fieldname": "against_pick_list_item",
-   "fieldtype": "Data",
-   "label": "Against Pick List Item",
-   "no_copy": 1,
-   "print_hide": 1,
-   "read_only": 1,
-   "report_hide": 1
+   "read_only_depends_on": "eval: (doc.delivered_qty > 0 || doc.from_voucher_type == \"Pick List\")"
   },
   {
    "fieldname": "column_break_7dxj",
@@ -297,6 +278,36 @@
   {
    "fieldname": "column_break_grdt",
    "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "from_voucher_type",
+   "fieldtype": "Select",
+   "label": "From Voucher Type",
+   "no_copy": 1,
+   "options": "\nPick List\nPurchase Receipt",
+   "print_hide": 1,
+   "read_only": 1,
+   "report_hide": 1
+  },
+  {
+   "fieldname": "from_voucher_detail_no",
+   "fieldtype": "Data",
+   "label": "From Voucher Detail No",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1,
+   "report_hide": 1
+  },
+  {
+   "fieldname": "from_voucher_no",
+   "fieldtype": "Dynamic Link",
+   "label": "From Voucher No",
+   "no_copy": 1,
+   "options": "from_voucher_type",
+   "print_hide": 1,
+   "read_only": 1,
+   "report_hide": 1,
+   "search_index": 1
   }
  ],
  "hide_toolbar": 1,
@@ -304,7 +315,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-08-08 17:15:13.317706",
+ "modified": "2023-10-19 16:41:16.545416",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Reservation Entry",
diff --git a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
index 936be3f..6b39965 100644
--- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
+++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
@@ -1,6 +1,8 @@
 # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
 
+from typing import Literal
+
 import frappe
 from frappe import _
 from frappe.model.document import Document
@@ -113,7 +115,7 @@
 		"""Auto pick Serial and Batch Nos to reserve when `Reservation Based On` is `Serial and Batch`."""
 
 		if (
-			not self.against_pick_list
+			not self.from_voucher_type
 			and (self.get("_action") == "submit")
 			and (self.has_serial_no or self.has_batch_no)
 			and cint(frappe.db.get_single_value("Stock Settings", "auto_reserve_serial_and_batch"))
@@ -239,7 +241,7 @@
 
 					if available_qty_to_reserve <= 0:
 						msg = _(
-							"Row #{0}: Stock not availabe to reserve for Item {1} against Batch {2} in Warehouse {3}."
+							"Row #{0}: Stock not available to reserve for Item {1} against Batch {2} in Warehouse {3}."
 						).format(
 							entry.idx,
 							frappe.bold(self.item_code),
@@ -316,21 +318,24 @@
 	) -> None:
 		"""Updates total reserved qty in the Pick List."""
 
-		if self.against_pick_list and self.against_pick_list_item:
+		if (
+			self.from_voucher_type == "Pick List" and self.from_voucher_no and self.from_voucher_detail_no
+		):
 			sre = frappe.qb.DocType("Stock Reservation Entry")
 			reserved_qty = (
 				frappe.qb.from_(sre)
 				.select(Sum(sre.reserved_qty))
 				.where(
 					(sre.docstatus == 1)
-					& (sre.against_pick_list == self.against_pick_list)
-					& (sre.against_pick_list_item == self.against_pick_list_item)
+					& (sre.from_voucher_type == "Pick List")
+					& (sre.from_voucher_no == self.from_voucher_no)
+					& (sre.from_voucher_detail_no == self.from_voucher_detail_no)
 				)
 			).run(as_list=True)[0][0] or 0
 
 			frappe.db.set_value(
 				"Pick List Item",
-				self.against_pick_list_item,
+				self.from_voucher_detail_no,
 				reserved_qty_field,
 				reserved_qty,
 				update_modified=update_modified,
@@ -365,7 +370,7 @@
 			).format(self.status, self.doctype)
 			frappe.throw(msg)
 
-		if self.against_pick_list:
+		if self.from_voucher_type == "Pick List":
 			msg = _(
 				"Stock Reservation Entry created against a Pick List cannot be updated. If you need to make changes, we recommend canceling the existing entry and creating a new one."
 			)
@@ -761,25 +766,27 @@
 
 
 def create_stock_reservation_entries_for_so_items(
-	so: object,
+	sales_order: object,
 	items_details: list[dict] = None,
-	against_pick_list: bool = False,
+	from_voucher_type: Literal["Pick List", "Purchase Receipt"] = None,
 	notify=True,
 ) -> None:
 	"""Creates Stock Reservation Entries for Sales Order Items."""
 
 	from erpnext.selling.doctype.sales_order.sales_order import get_unreserved_qty
 
-	if not against_pick_list and (
-		so.get("_action") == "submit"
-		and so.set_warehouse
-		and cint(frappe.get_cached_value("Warehouse", so.set_warehouse, "is_group"))
+	if not from_voucher_type and (
+		sales_order.get("_action") == "submit"
+		and sales_order.set_warehouse
+		and cint(frappe.get_cached_value("Warehouse", sales_order.set_warehouse, "is_group"))
 	):
 		return frappe.msgprint(
-			_("Stock cannot be reserved in the group warehouse {0}.").format(frappe.bold(so.set_warehouse))
+			_("Stock cannot be reserved in the group warehouse {0}.").format(
+				frappe.bold(sales_order.set_warehouse)
+			)
 		)
 
-	validate_stock_reservation_settings(so)
+	validate_stock_reservation_settings(sales_order)
 
 	allow_partial_reservation = frappe.db.get_single_value(
 		"Stock Settings", "allow_partial_reservation"
@@ -788,38 +795,36 @@
 	items = []
 	if items_details:
 		for item in items_details:
-			so_item = frappe.get_doc(
-				"Sales Order Item", item.get("sales_order_item") if against_pick_list else item.get("name")
-			)
-			so_item.reserve_stock = 1
+			so_item = frappe.get_doc("Sales Order Item", item.get("name"))
 			so_item.warehouse = item.get("warehouse")
 			so_item.qty_to_reserve = (
-				item.get("picked_qty") - item.get("stock_reserved_qty", 0)
-				if against_pick_list
-				else (flt(item.get("qty_to_reserve")) * flt(so_item.conversion_factor, 1))
+				flt(item.get("qty_to_reserve"))
+				if from_voucher_type in ["Pick List", "Purchase Receipt"]
+				else (
+					flt(item.get("qty_to_reserve"))
+					* (flt(item.get("conversion_factor")) or flt(so_item.conversion_factor) or 1)
+				)
 			)
-
-			if against_pick_list:
-				so_item.pick_list = item.get("parent")
-				so_item.pick_list_item = item.get("name")
-				so_item.pick_list_sbb = item.get("serial_and_batch_bundle")
+			so_item.from_voucher_no = item.get("from_voucher_no")
+			so_item.from_voucher_detail_no = item.get("from_voucher_detail_no")
+			so_item.serial_and_batch_bundle = item.get("serial_and_batch_bundle")
 
 			items.append(so_item)
 
 	sre_count = 0
-	reserved_qty_details = get_sre_reserved_qty_details_for_voucher("Sales Order", so.name)
+	reserved_qty_details = get_sre_reserved_qty_details_for_voucher("Sales Order", sales_order.name)
 
-	for item in items if items_details else so.get("items"):
+	for item in items if items_details else sales_order.get("items"):
 		# Skip if `Reserved Stock` is not checked for the item.
 		if not item.get("reserve_stock"):
 			continue
 
 		# Stock should be reserved from the Pick List if has Picked Qty.
-		if not against_pick_list and flt(item.picked_qty) > 0:
+		if not from_voucher_type == "Pick List" and flt(item.picked_qty) > 0:
 			frappe.throw(
-				_(
-					"Row #{0}: Item {1} has been picked, please create a Stock Reservation from the Pick List."
-				).format(item.idx, frappe.bold(item.item_code))
+				_("Row #{0}: Item {1} has been picked, please reserve stock from the Pick List.").format(
+					item.idx, frappe.bold(item.item_code)
+				)
 			)
 
 		is_stock_item, has_serial_no, has_batch_no = frappe.get_cached_value(
@@ -828,13 +833,15 @@
 
 		# Skip if Non-Stock Item.
 		if not is_stock_item:
-			frappe.msgprint(
-				_("Row #{0}: Stock cannot be reserved for a non-stock Item {1}").format(
-					item.idx, frappe.bold(item.item_code)
-				),
-				title=_("Stock Reservation"),
-				indicator="yellow",
-			)
+			if not from_voucher_type:
+				frappe.msgprint(
+					_("Row #{0}: Stock cannot be reserved for a non-stock Item {1}").format(
+						item.idx, frappe.bold(item.item_code)
+					),
+					title=_("Stock Reservation"),
+					indicator="yellow",
+				)
+
 			item.db_set("reserve_stock", 0)
 			continue
 
@@ -853,13 +860,15 @@
 
 		# Stock is already reserved for the item, notify the user and skip the item.
 		if unreserved_qty <= 0:
-			frappe.msgprint(
-				_("Row #{0}: Stock is already reserved for the Item {1}.").format(
-					item.idx, frappe.bold(item.item_code)
-				),
-				title=_("Stock Reservation"),
-				indicator="yellow",
-			)
+			if not from_voucher_type:
+				frappe.msgprint(
+					_("Row #{0}: Stock is already reserved for the Item {1}.").format(
+						item.idx, frappe.bold(item.item_code)
+					),
+					title=_("Stock Reservation"),
+					indicator="yellow",
+				)
+
 			continue
 
 		available_qty_to_reserve = get_available_qty_to_reserve(item.item_code, item.warehouse)
@@ -867,7 +876,7 @@
 		# No stock available to reserve, notify the user and skip the item.
 		if available_qty_to_reserve <= 0:
 			frappe.msgprint(
-				_("Row #{0}: No available stock to reserve for the Item {1} in Warehouse {2}.").format(
+				_("Row #{0}: Stock not available to reserve for the Item {1} in Warehouse {2}.").format(
 					item.idx, frappe.bold(item.item_code), frappe.bold(item.warehouse)
 				),
 				title=_("Stock Reservation"),
@@ -893,7 +902,9 @@
 
 		# Partial Reservation
 		if qty_to_be_reserved < unreserved_qty:
-			if not item.get("qty_to_reserve") or qty_to_be_reserved < flt(item.get("qty_to_reserve")):
+			if not from_voucher_type and (
+				not item.get("qty_to_reserve") or qty_to_be_reserved < flt(item.get("qty_to_reserve"))
+			):
 				msg = _("Row #{0}: Only {1} available to reserve for the Item {2}").format(
 					item.idx,
 					frappe.bold(str(qty_to_be_reserved / item.conversion_factor) + " " + item.uom),
@@ -915,33 +926,42 @@
 		sre.warehouse = item.warehouse
 		sre.has_serial_no = has_serial_no
 		sre.has_batch_no = has_batch_no
-		sre.voucher_type = so.doctype
-		sre.voucher_no = so.name
+		sre.voucher_type = sales_order.doctype
+		sre.voucher_no = sales_order.name
 		sre.voucher_detail_no = item.name
 		sre.available_qty = available_qty_to_reserve
 		sre.voucher_qty = item.stock_qty
 		sre.reserved_qty = qty_to_be_reserved
-		sre.company = so.company
+		sre.company = sales_order.company
 		sre.stock_uom = item.stock_uom
-		sre.project = so.project
+		sre.project = sales_order.project
 
-		if against_pick_list:
-			sre.against_pick_list = item.pick_list
-			sre.against_pick_list_item = item.pick_list_item
+		if from_voucher_type:
+			sre.from_voucher_type = from_voucher_type
+			sre.from_voucher_no = item.from_voucher_no
+			sre.from_voucher_detail_no = item.from_voucher_detail_no
 
-			if item.pick_list_sbb:
-				sbb = frappe.get_doc("Serial and Batch Bundle", item.pick_list_sbb)
-				sre.reservation_based_on = "Serial and Batch"
-				for entry in sbb.entries:
-					sre.append(
-						"sb_entries",
-						{
-							"serial_no": entry.serial_no,
-							"batch_no": entry.batch_no,
-							"qty": 1 if has_serial_no else abs(entry.qty),
-							"warehouse": entry.warehouse,
-						},
-					)
+		if item.get("serial_and_batch_bundle"):
+			sbb = frappe.get_doc("Serial and Batch Bundle", item.serial_and_batch_bundle)
+			sre.reservation_based_on = "Serial and Batch"
+
+			index, picked_qty = 0, 0
+			while index < len(sbb.entries) and picked_qty < qty_to_be_reserved:
+				entry = sbb.entries[index]
+				qty = 1 if has_serial_no else min(abs(entry.qty), qty_to_be_reserved - picked_qty)
+
+				sre.append(
+					"sb_entries",
+					{
+						"serial_no": entry.serial_no,
+						"batch_no": entry.batch_no,
+						"qty": qty,
+						"warehouse": entry.warehouse,
+					},
+				)
+
+				index += 1
+				picked_qty += qty
 
 		sre.save()
 		sre.submit()
@@ -956,29 +976,37 @@
 	voucher_type: str = None,
 	voucher_no: str = None,
 	voucher_detail_no: str = None,
-	against_pick_list: str = None,
+	from_voucher_type: Literal["Pick List", "Purchase Receipt"] = None,
+	from_voucher_no: str = None,
+	from_voucher_detail_no: str = None,
 	sre_list: list[dict] = None,
 	notify: bool = True,
 ) -> None:
 	"""Cancel Stock Reservation Entries."""
 
-	if not sre_list and against_pick_list:
-		sre = frappe.qb.DocType("Stock Reservation Entry")
-		sre_list = (
-			frappe.qb.from_(sre)
-			.select(sre.name)
-			.where(
-				(sre.docstatus == 1)
-				& (sre.against_pick_list == against_pick_list)
-				& (sre.status.notin(["Delivered", "Cancelled"]))
+	if not sre_list:
+		if voucher_type and voucher_no:
+			sre_list = get_stock_reservation_entries_for_voucher(
+				voucher_type, voucher_no, voucher_detail_no, fields=["name"]
 			)
-			.orderby(sre.creation)
-		).run(as_dict=True)
+		elif from_voucher_type and from_voucher_no:
+			sre = frappe.qb.DocType("Stock Reservation Entry")
+			query = (
+				frappe.qb.from_(sre)
+				.select(sre.name)
+				.where(
+					(sre.docstatus == 1)
+					& (sre.from_voucher_type == from_voucher_type)
+					& (sre.from_voucher_no == from_voucher_no)
+					& (sre.status.notin(["Delivered", "Cancelled"]))
+				)
+				.orderby(sre.creation)
+			)
 
-	elif not sre_list and (voucher_type and voucher_no):
-		sre_list = get_stock_reservation_entries_for_voucher(
-			voucher_type, voucher_no, voucher_detail_no, fields=["name"]
-		)
+			if from_voucher_detail_no:
+				query = query.where(sre.from_voucher_detail_no == from_voucher_detail_no)
+
+			sre_list = query.run(as_dict=True)
 
 	if sre_list:
 		for sre in sre_list:
diff --git a/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py b/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py
index 1168a4e..f4c74a8 100644
--- a/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py
+++ b/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py
@@ -5,6 +5,7 @@
 
 import frappe
 from frappe.tests.utils import FrappeTestCase, change_settings
+from frappe.utils import today
 
 from erpnext.selling.doctype.sales_order.sales_order import create_pick_list, make_delivery_note
 from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
@@ -28,10 +29,6 @@
 			items={self.sr_item.name: self.sr_item}, warehouse=self.warehouse, qty=100
 		)
 
-	def tearDown(self) -> None:
-		cancel_all_stock_reservation_entries()
-		return super().tearDown()
-
 	@change_settings("Stock Settings", {"allow_negative_stock": 0})
 	def test_validate_stock_reservation_settings(self) -> None:
 		from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
@@ -555,8 +552,9 @@
 						(sre.voucher_type == "Sales Order")
 						& (sre.voucher_no == location.sales_order)
 						& (sre.voucher_detail_no == location.sales_order_item)
-						& (sre.against_pick_list == pl.name)
-						& (sre.against_pick_list_item == location.name)
+						& (sre.from_voucher_type == "Pick List")
+						& (sre.from_voucher_no == pl.name)
+						& (sre.from_voucher_detail_no == location.name)
 					)
 				).run(as_dict=True)
 				reserved_sb_details: set[tuple] = {
@@ -567,6 +565,90 @@
 				# Test - 3: Reserved Serial/Batch Nos should be equal to Picked Serial/Batch Nos.
 				self.assertSetEqual(picked_sb_details, reserved_sb_details)
 
+	@change_settings(
+		"Stock Settings",
+		{
+			"allow_negative_stock": 0,
+			"enable_stock_reservation": 1,
+			"auto_reserve_serial_and_batch": 1,
+			"pick_serial_and_batch_based_on": "FIFO",
+			"auto_reserve_stock_for_sales_order_on_purchase": 1,
+		},
+	)
+	def test_stock_reservation_from_purchase_receipt(self):
+		from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
+		from erpnext.selling.doctype.sales_order.sales_order import make_material_request
+		from erpnext.stock.doctype.material_request.material_request import make_purchase_order
+
+		items_details = create_items()
+		create_material_receipt(items_details, self.warehouse, qty=10)
+
+		item_list = []
+		for item_code, properties in items_details.items():
+			item_list.append(
+				{
+					"item_code": item_code,
+					"warehouse": self.warehouse,
+					"qty": randint(11, 100),
+					"uom": properties.stock_uom,
+					"rate": randint(10, 400),
+				}
+			)
+
+		so = make_sales_order(
+			item_list=item_list,
+			warehouse=self.warehouse,
+		)
+
+		mr = make_material_request(so.name)
+		mr.schedule_date = today()
+		mr.save().submit()
+
+		po = make_purchase_order(mr.name)
+		po.supplier = "_Test Supplier"
+		po.save().submit()
+
+		pr = make_purchase_receipt(po.name)
+		pr.save().submit()
+
+		for item in pr.items:
+			sre, status, reserved_qty = frappe.db.get_value(
+				"Stock Reservation Entry",
+				{
+					"from_voucher_type": "Purchase Receipt",
+					"from_voucher_no": pr.name,
+					"from_voucher_detail_no": item.name,
+				},
+				["name", "status", "reserved_qty"],
+			)
+
+			# Test - 1: SRE status should be `Reserved`.
+			self.assertEqual(status, "Reserved")
+
+			# Test - 2: SRE Reserved Qty should be equal to PR Item Qty.
+			self.assertEqual(reserved_qty, item.qty)
+
+			if item.serial_and_batch_bundle:
+				sb_details = frappe.db.get_all(
+					"Serial and Batch Entry",
+					filters={"parent": item.serial_and_batch_bundle},
+					fields=["serial_no", "batch_no", "qty"],
+					as_list=True,
+				)
+				reserved_sb_details = frappe.db.get_all(
+					"Serial and Batch Entry",
+					filters={"parent": sre},
+					fields=["serial_no", "batch_no", "qty"],
+					as_list=True,
+				)
+
+				# Test - 3: Reserved Serial/Batch Nos should be equal to PR Item Serial/Batch Nos.
+				self.assertEqual(set(sb_details), set(reserved_sb_details))
+
+	def tearDown(self) -> None:
+		cancel_all_stock_reservation_entries()
+		return super().tearDown()
+
 
 def create_items() -> dict:
 	items_properties = [
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json
index 2052daa..1228290 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.json
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.json
@@ -38,8 +38,8 @@
   "stock_reservation_tab",
   "enable_stock_reservation",
   "column_break_rx3e",
-  "auto_reserve_stock_for_sales_order",
   "allow_partial_reservation",
+  "auto_reserve_stock_for_sales_order_on_purchase",
   "serial_and_batch_reservation_section",
   "auto_reserve_serial_and_batch",
   "serial_and_batch_item_settings_tab",
@@ -65,8 +65,7 @@
   "stock_frozen_upto_days",
   "column_break_26",
   "role_allowed_to_create_edit_back_dated_transactions",
-  "stock_auth_role",
-  "section_break_plhx"
+  "stock_auth_role"
  ],
  "fields": [
   {
@@ -356,7 +355,7 @@
   {
    "default": "1",
    "depends_on": "eval: doc.enable_stock_reservation",
-   "description": "If enabled, <b>Partial Stock Reservation Entries</b> can be created. For example, If you have a Sales Order of 100 units and the Available Stock is 90 units then a Stock Reservation Entry will be created for 90 units. ",
+   "description": "Partial stock can be reserved. For example, If you have a Sales Order of 100 units and the Available Stock is 90 units then a Stock Reservation Entry will be created for 90 units. ",
    "fieldname": "allow_partial_reservation",
    "fieldtype": "Check",
    "label": "Allow Partial Reservation"
@@ -383,7 +382,7 @@
   {
    "default": "1",
    "depends_on": "eval: doc.enable_stock_reservation",
-   "description": "If enabled, Serial and Batch Nos will be auto-reserved based on <b>Pick Serial / Batch Based On</b>",
+   "description": "Serial and Batch Nos will be auto-reserved based on <b>Pick Serial / Batch Based On</b>",
    "fieldname": "auto_reserve_serial_and_batch",
    "fieldtype": "Check",
    "label": "Auto Reserve Serial and Batch Nos"
@@ -394,14 +393,6 @@
    "label": "Serial and Batch Reservation"
   },
   {
-   "default": "0",
-   "depends_on": "eval: doc.enable_stock_reservation",
-   "description": "If enabled, <b>Stock Reservation Entries</b> will be created on submission of <b>Sales Order</b>",
-   "fieldname": "auto_reserve_stock_for_sales_order",
-   "fieldtype": "Check",
-   "label": "Auto Reserve Stock for Sales Order"
-  },
-  {
    "fieldname": "conversion_factor_section",
    "fieldtype": "Section Break",
    "label": "Stock UOM Quantity"
@@ -421,6 +412,14 @@
    "fieldname": "allow_to_edit_stock_uom_qty_for_purchase",
    "fieldtype": "Check",
    "label": "Allow to Edit Stock UOM Qty for Purchase Documents"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval: doc.enable_stock_reservation",
+   "description": "Stock will be reserved on submission of <b>Purchase Receipt</b> created against Material Receipt for Sales Order.",
+   "fieldname": "auto_reserve_stock_for_sales_order_on_purchase",
+   "fieldtype": "Check",
+   "label": "Auto Reserve Stock for Sales Order on Purchase"
   }
  ],
  "icon": "icon-cog",
@@ -428,7 +427,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2023-10-01 14:22:36.136111",
+ "modified": "2023-10-18 12:35:30.068799",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Settings",
@@ -453,4 +452,4 @@
  "sort_order": "ASC",
  "states": [],
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index a6ab63b..e29fc88 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -268,7 +268,7 @@
 	if not item:
 		item = frappe.get_doc("Item", args.get("item_code"))
 
-	if item.variant_of:
+	if item.variant_of and not item.taxes:
 		item.update_template_tables()
 
 	item_defaults = get_item_defaults(item.name, args.company)
@@ -330,8 +330,12 @@
 			),
 			"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),
+			"discount_account": get_default_discount_account(
+				args, item_defaults, item_group_defaults, brand_defaults
+			),
+			"provisional_expense_account": get_provisional_account(
+				args, item_defaults, item_group_defaults, brand_defaults
+			),
 			"cost_center": get_default_cost_center(
 				args, item_defaults, item_group_defaults, brand_defaults
 			),
@@ -606,6 +610,7 @@
 
 	# all templates have validity and no template is valid
 	if not taxes_with_validity and (not taxes_with_no_validity):
+		out["item_tax_template"] = ""
 		return None
 
 	# do not change if already a valid template
@@ -685,12 +690,22 @@
 	)
 
 
-def get_provisional_account(args, item):
-	return item.get("default_provisional_account") or args.default_provisional_account
+def get_provisional_account(args, item, item_group, brand):
+	return (
+		item.get("default_provisional_account")
+		or item_group.get("default_provisional_account")
+		or brand.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
+def get_default_discount_account(args, item, item_group, brand):
+	return (
+		item.get("default_discount_account")
+		or item_group.get("default_discount_account")
+		or brand.get("default_discount_account")
+		or args.discount_account
+	)
 
 
 def get_default_deferred_account(args, item, fieldname=None):
@@ -737,6 +752,12 @@
 			data = frappe.get_attr(path)(args.get("item_code"), company)
 
 			if data and (data.selling_cost_center or data.buying_cost_center):
+				if args.get("customer") and data.selling_cost_center:
+					return data.selling_cost_center
+
+				elif args.get("supplier") and data.buying_cost_center:
+					return data.buying_cost_center
+
 				return data.selling_cost_center or data.buying_cost_center
 
 	if not cost_center and args.get("cost_center"):
diff --git a/erpnext/stock/report/reserved_stock/reserved_stock.js b/erpnext/stock/report/reserved_stock/reserved_stock.js
index 2199f52..6872741 100644
--- a/erpnext/stock/report/reserved_stock/reserved_stock.js
+++ b/erpnext/stock/report/reserved_stock/reserved_stock.js
@@ -91,16 +91,30 @@
 			},
 		},
 		{
-			fieldname: "against_pick_list",
-			label: __("Against Pick List"),
+			fieldname: "from_voucher_type",
+			label: __("From Voucher Type"),
 			fieldtype: "Link",
-			options: "Pick List",
+			options: "DocType",
+			get_query: () => ({
+				filters: {
+					name: ["in", ["Pick List", "Purchase Receipt"]],
+				}
+			}),
+		},
+		{
+			fieldname: "from_voucher_no",
+			label: __("From Voucher No"),
+			fieldtype: "Dynamic Link",
+			options: "from_voucher_type",
 			get_query: () => ({
 				filters: {
 					docstatus: 1,
 					company: frappe.query_report.get_filter_value("company"),
 				},
 			}),
+			get_options: function () {
+				return frappe.query_report.get_filter_value("from_voucher_type");
+			},
 		},
 		{
 			fieldname: "reservation_based_on",
diff --git a/erpnext/stock/report/reserved_stock/reserved_stock.py b/erpnext/stock/report/reserved_stock/reserved_stock.py
index d93ee1c..21ce203 100644
--- a/erpnext/stock/report/reserved_stock/reserved_stock.py
+++ b/erpnext/stock/report/reserved_stock/reserved_stock.py
@@ -44,7 +44,8 @@
 			(sre.available_qty - sre.reserved_qty).as_("available_qty"),
 			sre.voucher_type,
 			sre.voucher_no,
-			sre.against_pick_list,
+			sre.from_voucher_type,
+			sre.from_voucher_no,
 			sre.name.as_("stock_reservation_entry"),
 			sre.status,
 			sre.project,
@@ -65,7 +66,8 @@
 		"warehouse",
 		"voucher_type",
 		"voucher_no",
-		"against_pick_list",
+		"from_voucher_type",
+		"from_voucher_no",
 		"reservation_based_on",
 		"status",
 		"project",
@@ -142,7 +144,6 @@
 			"fieldname": "voucher_type",
 			"label": _("Voucher Type"),
 			"fieldtype": "Data",
-			"options": "Warehouse",
 			"width": 110,
 		},
 		{
@@ -153,11 +154,17 @@
 			"width": 120,
 		},
 		{
-			"fieldname": "against_pick_list",
-			"label": _("Against Pick List"),
-			"fieldtype": "Link",
-			"options": "Pick List",
-			"width": 130,
+			"fieldname": "from_voucher_type",
+			"label": _("From Voucher Type"),
+			"fieldtype": "Data",
+			"width": 110,
+		},
+		{
+			"fieldname": "from_voucher_no",
+			"label": _("From Voucher No"),
+			"fieldtype": "Dynamic Link",
+			"options": "from_voucher_type",
+			"width": 120,
 		},
 		{
 			"fieldname": "stock_reservation_entry",
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index d3807b0..b950f18 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -2,6 +2,7 @@
 # License: GNU General Public License v3. See license.txt
 
 import copy
+import gzip
 import json
 from typing import Optional, Set, Tuple
 
@@ -10,20 +11,11 @@
 from frappe.model.meta import get_field_precision
 from frappe.query_builder import Case
 from frappe.query_builder.functions import CombineDatetime, Sum
-from frappe.utils import (
-	cint,
-	flt,
-	get_link_to_form,
-	getdate,
-	gzip_compress,
-	gzip_decompress,
-	now,
-	nowdate,
-	parse_json,
-)
+from frappe.utils import cint, flt, get_link_to_form, getdate, now, nowdate, parse_json
 
 import erpnext
 from erpnext.stock.doctype.bin.bin import update_qty as update_bin_qty
+from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions
 from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
 	get_sre_reserved_qty_for_item_and_warehouse as get_reserved_stock,
 )
@@ -294,7 +286,7 @@
 
 	attached_file = frappe.get_doc("File", file_name)
 
-	data = gzip_decompress(attached_file.get_content())
+	data = gzip.decompress(attached_file.get_content())
 	if data := json.loads(data.decode("utf-8")):
 		data = data
 
@@ -377,7 +369,7 @@
 
 def create_json_gz_file(data, doc, file_name=None) -> str:
 	encoded_content = frappe.safe_encode(frappe.as_json(data))
-	compressed_content = gzip_compress(encoded_content)
+	compressed_content = gzip.compress(encoded_content)
 
 	if not file_name:
 		json_filename = f"{scrub(doc.doctype)}-{scrub(doc.name)}.json.gz"
@@ -711,10 +703,17 @@
 		):
 			sle.outgoing_rate = get_incoming_rate_for_inter_company_transfer(sle)
 
+		dimensions = get_inventory_dimensions()
+		has_dimensions = False
+		if dimensions:
+			for dimension in dimensions:
+				if sle.get(dimension.get("fieldname")):
+					has_dimensions = True
+
 		if sle.serial_and_batch_bundle:
 			self.calculate_valuation_for_serial_batch_bundle(sle)
 		else:
-			if sle.voucher_type == "Stock Reconciliation" and not sle.batch_no:
+			if sle.voucher_type == "Stock Reconciliation" and not sle.batch_no and not has_dimensions:
 				# assert
 				self.wh_data.valuation_rate = sle.valuation_rate
 				self.wh_data.qty_after_transaction = sle.qty_after_transaction
@@ -1297,7 +1296,7 @@
 	return sle[0] if sle else frappe._dict()
 
 
-def get_previous_sle(args, for_update=False):
+def get_previous_sle(args, for_update=False, extra_cond=None):
 	"""
 	get the last sle on or before the current time-bucket,
 	to get actual qty before transaction, this function
@@ -1312,7 +1311,9 @@
 	}
 	"""
 	args["name"] = args.get("sle", None) or ""
-	sle = get_stock_ledger_entries(args, "<=", "desc", "limit 1", for_update=for_update)
+	sle = get_stock_ledger_entries(
+		args, "<=", "desc", "limit 1", for_update=for_update, extra_cond=extra_cond
+	)
 	return sle and sle[0] or {}
 
 
@@ -1324,6 +1325,7 @@
 	for_update=False,
 	debug=False,
 	check_serial_no=True,
+	extra_cond=None,
 ):
 	"""get stock ledger entries filtered by specific posting datetime conditions"""
 	conditions = " and timestamp(posting_date, posting_time) {0} timestamp(%(posting_date)s, %(posting_time)s)".format(
@@ -1361,6 +1363,9 @@
 	if operator in (">", "<=") and previous_sle.get("name"):
 		conditions += " and name!=%(name)s"
 
+	if extra_cond:
+		conditions += f"{extra_cond}"
+
 	return frappe.db.sql(
 		"""
 		select *, timestamp(posting_date, posting_time) as "timestamp"
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 0244406..bd0d469 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -95,6 +95,7 @@
 	posting_time=None,
 	with_valuation_rate=False,
 	with_serial_no=False,
+	inventory_dimensions_dict=None,
 ):
 	"""Returns stock balance quantity at given warehouse on given posting date or current date.
 
@@ -114,7 +115,13 @@
 		"posting_time": posting_time,
 	}
 
-	last_entry = get_previous_sle(args)
+	extra_cond = ""
+	if inventory_dimensions_dict:
+		for field, value in inventory_dimensions_dict.items():
+			args[field] = value
+			extra_cond += f" and {field} = %({field})s"
+
+	last_entry = get_previous_sle(args, extra_cond=extra_cond)
 
 	if with_valuation_rate:
 		if with_serial_no:
diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js
index f2b395a..587a3b4 100644
--- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js
+++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js
@@ -107,7 +107,7 @@
 	get_materials_from_supplier: function (frm) {
 		let sco_rm_details = [];
 
-		if (frm.doc.status != "Closed" && frm.doc.supplied_items && frm.doc.per_received > 0) {
+		if (frm.doc.status != "Closed" && frm.doc.supplied_items) {
 			frm.doc.supplied_items.forEach(d => {
 				if (d.total_supplied_qty > 0 && d.total_supplied_qty != d.consumed_qty) {
 					sco_rm_details.push(d.name);
@@ -193,7 +193,7 @@
 	}
 
 	has_unsupplied_items() {
-		return this.frm.doc['supplied_items'].some(item => item.required_qty > item.supplied_qty);
+		return this.frm.doc['supplied_items'].some(item => item.required_qty > (item.supplied_qty - item.returned_qty));
 	}
 
 	make_subcontracting_receipt() {
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
index 6aecaf9..7e06444 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
@@ -410,7 +410,6 @@
 
 	def make_item_gl_entries(self, gl_entries, warehouse_account=None):
 		stock_rbnb = self.get_company_default("stock_received_but_not_billed")
-		expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
 
 		warehouse_with_no_account = []
 
@@ -482,10 +481,7 @@
 					divisional_loss = flt(item.amount - stock_value_diff, item.precision("amount"))
 
 					if divisional_loss:
-						if self.is_return:
-							loss_account = expenses_included_in_valuation
-						else:
-							loss_account = item.expense_account
+						loss_account = item.expense_account
 
 						self.add_gl_entry(
 							gl_entries=gl_entries,
diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js
index d4daacd..f96823b 100644
--- a/erpnext/support/doctype/issue/issue.js
+++ b/erpnext/support/doctype/issue/issue.js
@@ -1,13 +1,6 @@
 frappe.ui.form.on("Issue", {
 	onload: function(frm) {
 		frm.email_field = "raised_by";
-		frm.set_query("customer", function () {
-			return {
-				filters: {
-					"disabled": 0
-				}
-			};
-		});
 
 		frappe.db.get_value("Support Settings", {name: "Support Settings"},
 			["allow_resetting_service_level_agreement", "track_service_level_agreement"], (r) => {
diff --git a/erpnext/templates/generators/item/item.html b/erpnext/templates/generators/item/item.html
deleted file mode 100644
index 358c1c5..0000000
--- a/erpnext/templates/generators/item/item.html
+++ /dev/null
@@ -1,80 +0,0 @@
-{% extends "templates/web.html" %}
-{% from "erpnext/templates/includes/macros.html" import recommended_item_row %}
-
-{% block title %} {{ title }} {% endblock %}
-
-{% block breadcrumbs %}
-<div class="item-breadcrumbs small text-muted">
-	{% include "templates/includes/breadcrumbs.html" %}
-</div>
-{% endblock %}
-
-{% block page_content %}
-<div class="product-container item-main">
-	{% from "erpnext/templates/includes/macros.html" import product_image %}
-	<div class="item-content">
-		<div class="product-page-content" itemscope itemtype="http://schema.org/Product">
-			<!-- Image, Description, Add to Cart -->
-			<div class="row mb-5">
-				{% include "templates/generators/item/item_image.html" %}
-				{% include "templates/generators/item/item_details.html" %}
-			</div>
-		</div>
-	</div>
-</div>
-
-<!-- Additional Info/Reviews, Recommendations -->
-<div class="d-flex">
-	{% set show_recommended_items = recommended_items and shopping_cart.cart_settings.enable_recommendations %}
-	{% set info_col = 'col-9' if show_recommended_items else 'col-12' %}
-
-	{% set padding_top = 'pt-0' if (show_tabs and tabs) else '' %}
-
-	<div class="product-container mt-4 {{ padding_top }} {{ info_col }}">
-		<div class="item-content {{ 'mt-minus-2' if (show_tabs and tabs) else '' }}">
-			<div class="product-page-content">
-				<!-- Product Specifications Table Section -->
-				{% if show_tabs and tabs %}
-					<div class="category-tabs">
-						<!-- tabs -->
-							{{ web_block("Section with Tabs", values=tabs, add_container=0,
-								add_top_padding=0, add_bottom_padding=0)
-							}}
-					</div>
-				{% elif website_specifications %}
-					{% include "templates/generators/item/item_specifications.html"%}
-				{% endif %}
-
-				<!-- Advanced Custom Website Content -->
-				{{ doc.website_content or '' }}
-
-				<!-- Reviews and Comments -->
-				{% if shopping_cart.cart_settings.enable_reviews and not doc.has_variants %}
-					{% include "templates/generators/item/item_reviews.html"%}
-				{% endif %}
-			</div>
-		</div>
-	</div>
-
-	<!-- Recommended Items -->
-	{% if show_recommended_items %}
-		<div class="mt-4 col-3 recommended-item-section">
-			<span class="recommendation-header">Recommended</span>
-			<div class="product-container mt-2 recommendation-container">
-				{% for item in recommended_items %}
-					{{ recommended_item_row(item) }}
-				{% endfor %}
-			</div>
-		</div>
-	{% endif %}
-
-</div>
-{% endblock %}
-
-{% block base_scripts %}
-<!-- js should be loaded in body! -->
-<script type="text/javascript" src="/assets/frappe/js/lib/jquery/jquery.min.js"></script>
-{{ include_script("frappe-web.bundle.js") }}
-{{ include_script("controls.bundle.js") }}
-{{ include_script("dialog.bundle.js") }}
-{% endblock %}
diff --git a/erpnext/templates/generators/item/item_add_to_cart.html b/erpnext/templates/generators/item/item_add_to_cart.html
deleted file mode 100644
index 9bd3f75..0000000
--- a/erpnext/templates/generators/item/item_add_to_cart.html
+++ /dev/null
@@ -1,180 +0,0 @@
-{% if shopping_cart and shopping_cart.cart_settings.enabled %}
-
-{% set cart_settings = shopping_cart.cart_settings %}
-{% set product_info = shopping_cart.product_info %}
-
-<div class="item-cart row mt-2" data-variant-item-code="{{ item_code }}">
-	<div class="col-md-12">
-		<!-- Price and Availability -->
-		{% if cart_settings.show_price and product_info.price %}
-			{% set price_info = product_info.price %}
-
-			<div class="product-price">
-				<!-- Final Price -->
-				<span itemprop="offers" itemscope itemtype="https://schema.org/Offer">
-					<span itemprop="price" content="{{ price_info.price_list_rate }}">{{ price_info.formatted_price_sales_uom }}</span>
-					<span style="display:none;" itemprop="priceCurrency" content="{{ price_info.currency }}">{{ price_info.currency }}</span>
-				</span>
-
-				<!-- Striked Price and Discount  -->
-				{% if price_info.formatted_mrp %}
-					<small class="formatted-price">
-						<s>MRP {{ price_info.formatted_mrp }}</s>
-					</small>
-					<small class="ml-1 formatted-price in-green">
-						-{{ price_info.get("formatted_discount_percent") or price_info.get("formatted_discount_rate")}}
-					</small>
-				{% endif %}
-
-				<!-- Price per UOM -->
-				<small class="formatted-price ml-2">
-					({{ price_info.formatted_price }} / {{ product_info.uom }})
-				</small>
-			</div>
-		{% else %}
-			{{ _("UOM") }} : {{ product_info.uom }}
-		{% endif %}
-
-		{% if cart_settings.show_stock_availability %}
-		<div class="mt-2">
-			{% if product_info.get("on_backorder") %}
-				<span class="no-stock out-of-stock" style="color: var(--primary-color);">
-					{{ _('Available on backorder') }}
-				</span>
-			{% elif product_info.in_stock == 0 %}
-				<span class="no-stock out-of-stock">
-					{{ _('Out of stock') }}
-				</span>
-			{% elif product_info.in_stock == 1 %}
-				<span class="in-green has-stock">
-					{{ _('In stock') }}
-					{% if product_info.show_stock_qty and product_info.stock_qty %}
-						({{ product_info.stock_qty }})
-					{% endif %}
-				</span>
-			{% endif %}
-		</div>
-		{% endif %}
-
-		<!-- Offers -->
-		{% if doc.offers %}
-			<br>
-			<div class="offers-heading mb-4">
-				<span class="mr-1 tag-icon">
-					<svg class="icon icon-lg"><use href="#icon-tag"></use></svg>
-				</span>
-				<b>Available Offers</b>
-			</div>
-			<div class="offer-container">
-				{% for offer in doc.offers %}
-				<div class="mt-2 d-flex">
-					<div class="mr-2" >
-						<svg width="24" height="24" viewBox="0 0 24 24" stroke="var(--yellow-500)" fill="none" xmlns="http://www.w3.org/2000/svg">
-							<path d="M19 15.6213C19 15.2235 19.158 14.842 19.4393 14.5607L20.9393 13.0607C21.5251 12.4749 21.5251 11.5251 20.9393 10.9393L19.4393 9.43934C19.158 9.15804 19 8.7765 19 8.37868V6.5C19 5.67157 18.3284 5 17.5 5H15.6213C15.2235 5 14.842 4.84196 14.5607 4.56066L13.0607 3.06066C12.4749 2.47487 11.5251 2.47487 10.9393 3.06066L9.43934 4.56066C9.15804 4.84196 8.7765 5 8.37868 5H6.5C5.67157 5 5 5.67157 5 6.5V8.37868C5 8.7765 4.84196 9.15804 4.56066 9.43934L3.06066 10.9393C2.47487 11.5251 2.47487 12.4749 3.06066 13.0607L4.56066 14.5607C4.84196 14.842 5 15.2235 5 15.6213V17.5C5 18.3284 5.67157 19 6.5 19H8.37868C8.7765 19 9.15804 19.158 9.43934 19.4393L10.9393 20.9393C11.5251 21.5251 12.4749 21.5251 13.0607 20.9393L14.5607 19.4393C14.842 19.158 15.2235 19 15.6213 19H17.5C18.3284 19 19 18.3284 19 17.5V15.6213Z" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
-							<path d="M15 9L9 15" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
-							<path d="M10.5 9.5C10.5 10.0523 10.0523 10.5 9.5 10.5C8.94772 10.5 8.5 10.0523 8.5 9.5C8.5 8.94772 8.94772 8.5 9.5 8.5C10.0523 8.5 10.5 8.94772 10.5 9.5Z" fill="white" stroke-linecap="round" stroke-linejoin="round"/>
-							<path d="M15.5 14.5C15.5 15.0523 15.0523 15.5 14.5 15.5C13.9477 15.5 13.5 15.0523 13.5 14.5C13.5 13.9477 13.9477 13.5 14.5 13.5C15.0523 13.5 15.5 13.9477 15.5 14.5Z" fill="white" stroke-linecap="round" stroke-linejoin="round"/>
-						</svg>
-					</div>
-					<p class="mr-1 mb-1">
-						{{ _(offer.offer_title) }}:
-						{{ _(offer.offer_subtitle) if offer.offer_subtitle else '' }}
-						<a class="offer-details" href="#"
-							data-offer-title="{{ offer.offer_title }}" data-offer-id="{{ offer.name }}"
-							role="button">
-							{{ _("More") }}
-						</a>
-					</p>
-				</div>
-				{% endfor %}
-			</div>
-		{% endif %}
-
-		<!-- Add to Cart / View in Cart, Contact Us -->
-		<div class="mt-6 mb-5">
-			<div class="mb-4 d-flex">
-				<!-- Add to Cart -->
-				{% if product_info.price and (cart_settings.allow_items_not_in_stock or product_info.in_stock) %}
-					<a href="/cart" class="btn btn-light btn-view-in-cart hidden mr-2 font-md"
-						role="button">
-						{{  _("View in Cart") if cart_settings.enable_checkout else _("View in Quote") }}
-					</a>
-					<button
-						data-item-code="{{item_code}}"
-						class="btn btn-primary btn-add-to-cart mr-2 w-30-40"
-					>
-						<span class="mr-2">
-							<svg class="icon icon-md">
-								<use href="#icon-assets"></use>
-							</svg>
-						</span>
-						{{ _("Add to Cart") if cart_settings.enable_checkout else  _("Add to Quote") }}
-					</button>
-				{% endif %}
-
-				<!-- Contact Us -->
-				{% if cart_settings.show_contact_us_button %}
-					{% include "templates/generators/item/item_inquiry.html" %}
-				{% endif %}
-			</div>
-		</div>
-	</div>
-</div>
-
-<script>
-	frappe.ready(() => {
-		$('.page_content').on('click', '.btn-add-to-cart', (e) => {
-			// Bind action on add to cart button
-			const $btn = $(e.currentTarget);
-			$btn.prop('disabled', true);
-			const item_code = $btn.data('item-code');
-			erpnext.e_commerce.shopping_cart.update_cart({
-				item_code,
-				qty: 1,
-				callback(r) {
-					$btn.prop('disabled', false);
-					if (r.message) {
-						$('.btn-add-to-cart, .btn-view-in-cart').toggleClass('hidden');
-					}
-				}
-			});
-		});
-
-		$('.page_content').on('click', '.offer-details', (e) => {
-			// Bind action on More link in Offers
-			const $btn = $(e.currentTarget);
-			$btn.prop('disabled', true);
-
-			var d = new frappe.ui.Dialog({
-				title: __($btn.data('offer-title')),
-				fields: [
-					{
-						fieldname: 'offer_details',
-						fieldtype: 'HTML'
-					},
-					{
-						fieldname: 'section_break',
-						fieldtype: 'Section Break'
-					}
-				]
-			});
-
-			frappe.call({
-				method: 'erpnext.e_commerce.doctype.website_offer.website_offer.get_offer_details',
-				args: {
-					offer_id: $btn.data('offer-id')
-				},
-				callback: (value) => {
-					d.set_value("offer_details", value.message);
-					d.show();
-					$btn.prop('disabled', false);
-				}
-			})
-
-		});
-	});
-
-
-</script>
-
-{% endif %}
diff --git a/erpnext/templates/generators/item/item_configure.html b/erpnext/templates/generators/item/item_configure.html
deleted file mode 100644
index e97a275..0000000
--- a/erpnext/templates/generators/item/item_configure.html
+++ /dev/null
@@ -1,20 +0,0 @@
-{% if shopping_cart and shopping_cart.cart_settings.enabled %}
-{% set cart_settings = shopping_cart.cart_settings %}
-
-<div class="mt-5 mb-6">
-	{% if cart_settings.enable_variants | int %}
-	<button class="btn btn-primary-light btn-configure font-md mr-2"
-		data-item-code="{{ doc.item_code }}"
-		data-item-name="{{ doc.web_item_name }}"
-	>
-		{{ _('Select Variant') }}
-	</button>
-	{% endif %}
-	{% if cart_settings.show_contact_us_button %}
-		{% include "templates/generators/item/item_inquiry.html" %}
-	{% endif %}
-</div>
-<script>
-{% include "templates/generators/item/item_configure.js" %}
-</script>
-{% endif %}
diff --git a/erpnext/templates/generators/item/item_configure.js b/erpnext/templates/generators/item/item_configure.js
deleted file mode 100644
index 9beba3f..0000000
--- a/erpnext/templates/generators/item/item_configure.js
+++ /dev/null
@@ -1,343 +0,0 @@
-class ItemConfigure {
-	constructor(item_code, item_name) {
-		this.item_code = item_code;
-		this.item_name = item_name;
-
-		this.get_attributes_and_values()
-			.then(attribute_data => {
-				this.attribute_data = attribute_data;
-				this.show_configure_dialog();
-			});
-	}
-
-	show_configure_dialog() {
-		const fields = this.attribute_data.map(a => {
-			return {
-				fieldtype: 'Select',
-				label: a.attribute,
-				fieldname: a.attribute,
-				options: a.values.map(v => {
-					return {
-						label: v,
-						value: v
-					};
-				}),
-				change: (e) => {
-					this.on_attribute_selection(e);
-				}
-			};
-		});
-
-		this.dialog = new frappe.ui.Dialog({
-			title: __('Select Variant for {0}', [this.item_name]),
-			fields,
-			on_hide: () => {
-				set_continue_configuration();
-			}
-		});
-
-		this.attribute_data.forEach(a => {
-			const field = this.dialog.get_field(a.attribute);
-			const $a = $(`<a href>${__("Clear")}</a>`);
-			$a.on('click', (e) => {
-				e.preventDefault();
-				this.dialog.set_value(a.attribute, '');
-			});
-			field.$wrapper.find('.help-box').append($a);
-		});
-
-		this.append_status_area();
-		this.dialog.show();
-
-		this.dialog.set_values(JSON.parse(localStorage.getItem(this.get_cache_key())));
-
-		$('.btn-configure').prop('disabled', false);
-	}
-
-	on_attribute_selection(e) {
-		if (e) {
-			const changed_fieldname = $(e.target).data('fieldname');
-			this.show_range_input_if_applicable(changed_fieldname);
-		} else {
-			this.show_range_input_for_all_fields();
-		}
-
-		const values = this.dialog.get_values();
-		if (Object.keys(values).length === 0) {
-			this.clear_status();
-			localStorage.removeItem(this.get_cache_key());
-			return;
-		}
-
-		// save state
-		localStorage.setItem(this.get_cache_key(), JSON.stringify(values));
-
-		// show
-		this.set_loading_status();
-
-		this.get_next_attribute_and_values(values)
-			.then(data => {
-				const {
-					valid_options_for_attributes,
-				} = data;
-
-				this.set_item_found_status(data);
-
-				for (let attribute in valid_options_for_attributes) {
-					const valid_options = valid_options_for_attributes[attribute];
-					const options = this.dialog.get_field(attribute).df.options;
-					const new_options = options.map(o => {
-						o.disabled = !valid_options.includes(o.value);
-						return o;
-					});
-
-					this.dialog.set_df_property(attribute, 'options', new_options);
-					this.dialog.get_field(attribute).set_options();
-				}
-			});
-	}
-
-	show_range_input_for_all_fields() {
-		this.dialog.fields.forEach(f => {
-			this.show_range_input_if_applicable(f.fieldname);
-		});
-	}
-
-	show_range_input_if_applicable(fieldname) {
-		const changed_field = this.dialog.get_field(fieldname);
-		const changed_value = changed_field.get_value();
-		if (changed_value && changed_value.includes(' to ')) {
-			// possible range input
-			let numbers = changed_value.split(' to ');
-			numbers = numbers.map(number => parseFloat(number));
-
-			if (!numbers.some(n => isNaN(n))) {
-				numbers.sort((a, b) => a - b);
-				if (changed_field.$input_wrapper.find('.range-selector').length) {
-					return;
-				}
-				const parent = $('<div class="range-selector">')
-					.insertBefore(changed_field.$input_wrapper.find('.help-box'));
-				const control = frappe.ui.form.make_control({
-					df: {
-						fieldtype: 'Int',
-						label: __('Enter value betweeen {0} and {1}', [numbers[0], numbers[1]]),
-						change: () => {
-							const value = control.get_value();
-							if (value < numbers[0] || value > numbers[1]) {
-								control.$wrapper.addClass('was-validated');
-								control.set_description(
-									__('Value must be between {0} and {1}', [numbers[0], numbers[1]]));
-								control.$input[0].setCustomValidity('error');
-							} else {
-								control.$wrapper.removeClass('was-validated');
-								control.set_description('');
-								control.$input[0].setCustomValidity('');
-								this.update_range_values(fieldname, value);
-							}
-						}
-					},
-					render_input: true,
-					parent
-				});
-				control.$wrapper.addClass('mt-3');
-			}
-		}
-	}
-
-	update_range_values(attribute, range_value) {
-		this.range_values = this.range_values || {};
-		this.range_values[attribute] = range_value;
-	}
-
-	show_remaining_optional_attributes() {
-		// show all attributes if remaining
-		// unselected attributes are all optional
-		const unselected_attributes = this.dialog.fields.filter(df => {
-			const value_selected = this.dialog.get_value(df.fieldname);
-			return !value_selected;
-		});
-		const is_optional_attribute = df => {
-			const optional_attributes = this.attribute_data
-				.filter(a => a.optional).map(a => a.attribute);
-			return optional_attributes.includes(df.fieldname);
-		};
-		if (unselected_attributes.every(is_optional_attribute)) {
-			unselected_attributes.forEach(df => {
-				this.dialog.fields_dict[df.fieldname].$wrapper.show();
-			});
-		}
-	}
-
-	set_loading_status() {
-		this.dialog.$status_area.html(`
-			<div class="alert alert-warning d-flex justify-content-between align-items-center" role="alert">
-				${__('Loading...')}
-			</div>
-		`);
-	}
-
-	set_item_found_status(data) {
-		const html = this.get_html_for_item_found(data);
-		this.dialog.$status_area.html(html);
-	}
-
-	clear_status() {
-		this.dialog.$status_area.empty();
-	}
-
-	get_html_for_item_found({ filtered_items_count, filtered_items, exact_match, product_info, available_qty, settings }) {
-		const one_item = exact_match.length === 1
-			? exact_match[0]
-			: filtered_items_count === 1
-				? filtered_items[0]
-				: '';
-
-		let item_add_to_cart = one_item ? `
-			<button data-item-code="${one_item}"
-				class="btn btn-primary btn-add-to-cart w-100"
-				data-action="btn_add_to_cart"
-			>
-				<span class="mr-2">
-					${frappe.utils.icon('assets', 'md')}
-				</span>
-				${__("Add to Cart")}
-			</button>
-		` : '';
-
-		const items_found = filtered_items_count === 1 ?
-			__('{0} item found.', [filtered_items_count]) :
-			__('{0} items found.', [filtered_items_count]);
-
-		/* eslint-disable indent */
-		const item_found_status = exact_match.length === 1
-			? `<div class="alert alert-success d-flex justify-content-between align-items-center" role="alert">
-				<div><div>
-					${one_item}
-					${product_info && product_info.price && !$.isEmptyObject(product_info.price)
-						? '(' + product_info.price.formatted_price_sales_uom + ')'
-						: ''
-					}
-
-					${available_qty === 0 && product_info && product_info?.is_stock_item
-						? '<span class="text-danger">(' + __('Out of Stock') + ')</span>' : ''}
-
-				</div></div>
-				<a href data-action="btn_clear_values" data-item-code="${one_item}">
-					${__('Clear Values')}
-				</a>
-			</div>`
-			: `<div class="alert alert-warning d-flex justify-content-between align-items-center" role="alert">
-					<span>
-						${items_found}
-					</span>
-					<a href data-action="btn_clear_values">
-						${__('Clear values')}
-					</a>
-			</div>`;
-		/* eslint-disable indent */
-
-		if (!product_info?.allow_items_not_in_stock && available_qty === 0
-			&& product_info && product_info?.is_stock_item) {
-			item_add_to_cart = '';
-		}
-
-		return `
-			${item_found_status}
-			${item_add_to_cart}
-		`;
-	}
-
-	btn_add_to_cart(e) {
-		if (frappe.session.user !== 'Guest') {
-			localStorage.removeItem(this.get_cache_key());
-		}
-		const item_code = $(e.currentTarget).data('item-code');
-		const additional_notes = Object.keys(this.range_values || {}).map(attribute => {
-			return `${attribute}: ${this.range_values[attribute]}`;
-		}).join('\n');
-		erpnext.e_commerce.shopping_cart.update_cart({
-			item_code,
-			additional_notes,
-			qty: 1
-		});
-		this.dialog.hide();
-	}
-
-	btn_clear_values() {
-		this.dialog.fields_list.forEach(f => {
-			if (f.df?.options) {
-				f.df.options = f.df.options.map(option => {
-					option.disabled = false;
-					return option;
-				});
-			}
-		});
-		this.dialog.clear();
-		this.dialog.$status_area.empty();
-		this.on_attribute_selection();
-	}
-
-	append_status_area() {
-		this.dialog.$status_area = $('<div class="status-area mt-5">');
-		this.dialog.$wrapper.find('.modal-body').append(this.dialog.$status_area);
-		this.dialog.$wrapper.on('click', '[data-action]', (e) => {
-			e.preventDefault();
-			const $target = $(e.currentTarget);
-			const action = $target.data('action');
-			const method = this[action];
-			method.call(this, e);
-		});
-		this.dialog.$wrapper.addClass('item-configurator-dialog');
-	}
-
-	get_next_attribute_and_values(selected_attributes) {
-		return this.call('erpnext.e_commerce.variant_selector.utils.get_next_attribute_and_values', {
-			item_code: this.item_code,
-			selected_attributes
-		});
-	}
-
-	get_attributes_and_values() {
-		return this.call('erpnext.e_commerce.variant_selector.utils.get_attributes_and_values', {
-			item_code: this.item_code
-		});
-	}
-
-	get_cache_key() {
-		return `configure:${this.item_code}`;
-	}
-
-	call(method, args) {
-		// promisified frappe.call
-		return new Promise((resolve, reject) => {
-			frappe.call(method, args)
-				.then(r => resolve(r.message))
-				.fail(reject);
-		});
-	}
-}
-
-function set_continue_configuration() {
-	const $btn_configure = $('.btn-configure');
-	const { itemCode } = $btn_configure.data();
-
-	if (localStorage.getItem(`configure:${itemCode}`)) {
-		$btn_configure.text(__('Continue Selection'));
-	} else {
-		$btn_configure.text(__('Select Variant'));
-	}
-}
-
-frappe.ready(() => {
-	const $btn_configure = $('.btn-configure');
-	if (!$btn_configure.length) return;
-	const { itemCode, itemName } = $btn_configure.data();
-
-	set_continue_configuration();
-
-	$btn_configure.on('click', () => {
-		$btn_configure.prop('disabled', true);
-		new ItemConfigure(itemCode, itemName);
-	});
-});
diff --git a/erpnext/templates/generators/item/item_details.html b/erpnext/templates/generators/item/item_details.html
deleted file mode 100644
index 028936b..0000000
--- a/erpnext/templates/generators/item/item_details.html
+++ /dev/null
@@ -1,63 +0,0 @@
-{% set width_class = "expand" if not slides else "" %}
-{% set cart_settings = shopping_cart.cart_settings %}
-{% set product_info = shopping_cart.product_info %}
-{% set price_info = product_info.get('price') or {} %}
-
-<div class="col-md-7 product-details {{ width_class }}">
-	<div class="d-flex">
-		<!-- title -->
-		<div class="product-title col-11" itemprop="name">
-			{{ doc.web_item_name }}
-		</div>
-
-		<!-- Wishlist -->
-		{% if cart_settings.enable_wishlist %}
-			<div class="like-action-item-fp like-action {{ 'like-action-wished' if wished else ''}} ml-2"
-				data-item-code="{{ doc.item_code }}">
-				<svg class="icon sm">
-					<use class="{{ 'wished' if wished else 'not-wished' }} wish-icon" href="#icon-heart"></use>
-				</svg>
-			</div>
-		{% endif %}
-	</div>
-
-	<p class="product-code">
-		<span class="product-item-group">
-			{{ _(doc.item_group) }}
-		</span>
-		<span class="product-item-code">
-			{{ _("Item Code") }}:
-		</span>
-		<span itemprop="productID">{{ doc.item_code }}</span>
-	</p>
-	{% if has_variants %}
-		<!-- configure template -->
-		{% include "templates/generators/item/item_configure.html" %}
-	{% else %}
-		<!-- add variant to cart -->
-		{% include "templates/generators/item/item_add_to_cart.html" %}
-	{% endif %}
-	<!-- description -->
-	<div class="product-description" itemprop="description">
-	{% if frappe.utils.strip_html(doc.web_long_description or '') %}
-		{{ doc.web_long_description | safe }}
-	{% elif frappe.utils.strip_html(doc.description or '')  %}
-		{{ doc.description | safe }}
-	{% else %}
-		{{ "" }}
-	{% endif  %}
-	</div>
-</div>
-
-{% block base_scripts %}
-<!-- js should be loaded in body! -->
-<script type="text/javascript" src="/assets/frappe/js/lib/jquery/jquery.min.js"></script>
-{% endblock %}
-
-<script>
-	$('.page_content').on('click', '.like-action-item-fp', (e) => {
-			// Bind action on wishlist button
-			const $btn = $(e.currentTarget);
-			erpnext.e_commerce.wishlist.wishlist_action($btn);
-		});
-</script>
\ No newline at end of file
diff --git a/erpnext/templates/generators/item/item_image.html b/erpnext/templates/generators/item/item_image.html
deleted file mode 100644
index e1bb3b9..0000000
--- a/erpnext/templates/generators/item/item_image.html
+++ /dev/null
@@ -1,108 +0,0 @@
-{% set column_size = 5 if slides else 4 %}
-<div class="col-md-{{ column_size }} h-100 d-flex mb-4">
-	{% if slides %}
-		<div class="item-slideshow d-flex flex-column mr-3">
-			{% for item in slides %}
-			<img class="item-slideshow-image mb-2 {% if loop.first %}active{% endif %}"
-					src="{{ item.image }}" alt="{{ item.heading }}">
-			{% endfor %}
-		</div>
-		{{ product_image(slides[0].image, 'product-image') }}
-		<!-- Simple image slideshow -->
-		<script>
-			frappe.ready(() => {
-				$('.page_content').on('click', '.item-slideshow-image', (e) => {
-					const $img = $(e.currentTarget);
-					const link = $img.prop('src');
-					const $product_image = $('.product-image');
-					$product_image.find('a').prop('href', link);
-					$product_image.find('img').prop('src', link);
-
-					$('.item-slideshow-image').removeClass('active');
-					$img.addClass('active');
-				});
-			})
-		</script>
-	{% else %}
-		{{ product_image(doc.website_image, alt=doc.website_image_alt or doc.item_name) }}
-	{% endif %}
-
-	<!-- Simple image preview -->
-
-	<div class="image-zoom-view" style="display: none;">
-		<button type="button" class="close" aria-label="Close">
-			<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
-			stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x">
-				<line x1="18" y1="6" x2="6" y2="18"></line>
-				<line x1="6" y1="6" x2="18" y2="18"></line>
-			</svg>
-		</button>
-	</div>
-</div>
-<style>
-	.website-image {
-		cursor: pointer;
-	}
-
-	.image-zoom-view {
-		position: fixed;
-		top: 0;
-		left: 0;
-		right: 0;
-		bottom: 0;
-		height: 100vh;
-		width: 100vw;
-		display: flex;
-		justify-content: center;
-		align-items: center;
-		background: rgba(0, 0, 0, 0.8);
-		z-index: 1080;
-	}
-
-	.image-zoom-view img {
-		max-height: 100%;
-		max-width: 100%;
-	}
-
-	.image-zoom-view button {
-		position: absolute;
-		right: 3rem;
-		top: 2rem;
-	}
-
-	.image-zoom-view svg {
-		color: var(--white);
-	}
-</style>
-<script>
-	frappe.ready(() => {
-		const $zoom_wrapper = $('.image-zoom-view');
-
-		$('.website-image').on('click', (e) => {
-			e.preventDefault();
-			const $img = $(e.target);
-			const src = $img.prop('src');
-			if (!src) return;
-			show_preview(src);
-		});
-
-		$zoom_wrapper.on('click', 'button', hide_preview);
-
-		$(document).on('keydown', (e) => {
-			if (e.key === 'Escape') {
-				hide_preview();
-			}
-		});
-
-		function show_preview(src) {
-			$zoom_wrapper.show();
-			const $img = $(`<img src="${src}">`)
-			$zoom_wrapper.append($img);
-		}
-
-		function hide_preview() {
-			$zoom_wrapper.find('img').remove();
-			$zoom_wrapper.hide();
-		}
-	})
-</script>
diff --git a/erpnext/templates/generators/item/item_inquiry.html b/erpnext/templates/generators/item/item_inquiry.html
deleted file mode 100644
index af636f1..0000000
--- a/erpnext/templates/generators/item/item_inquiry.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% if shopping_cart and shopping_cart.cart_settings.enabled %}
-{% set cart_settings = shopping_cart.cart_settings %}
-	{% if cart_settings.show_contact_us_button | int %}
-		<button class="btn btn-inquiry font-md w-30-40" data-item-code="{{ doc.name }}">
-			{{ _('Contact Us') }}
-		</button>
-	{% endif %}
-<script>
-{% include "templates/generators/item/item_inquiry.js" %}
-</script>
-{% endif %}
diff --git a/erpnext/templates/generators/item/item_inquiry.js b/erpnext/templates/generators/item/item_inquiry.js
deleted file mode 100644
index 0aee996..0000000
--- a/erpnext/templates/generators/item/item_inquiry.js
+++ /dev/null
@@ -1,77 +0,0 @@
-frappe.ready(() => {
-	const d = new frappe.ui.Dialog({
-		title: __('Contact Us'),
-		fields: [
-			{
-				fieldtype: 'Data',
-				label: __('Full Name'),
-				fieldname: 'lead_name',
-				reqd: 1
-			},
-			{
-				fieldtype: 'Data',
-				label: __('Organization Name'),
-				fieldname: 'company_name',
-			},
-			{
-				fieldtype: 'Data',
-				label: __('Email'),
-				fieldname: 'email_id',
-				options: 'Email',
-				reqd: 1
-			},
-			{
-				fieldtype: 'Data',
-				label: __('Phone Number'),
-				fieldname: 'phone',
-				options: 'Phone',
-				reqd: 1
-			},
-			{
-				fieldtype: 'Data',
-				label: __('Subject'),
-				fieldname: 'subject',
-				reqd: 1
-			},
-			{
-				fieldtype: 'Text',
-				label: __('Message'),
-				fieldname: 'message',
-				reqd: 1
-			}
-		],
-		primary_action: send_inquiry,
-		primary_action_label: __('Send')
-	});
-
-	function send_inquiry() {
-		const values = d.get_values();
-		const doc = Object.assign({}, values);
-		delete doc.subject;
-		delete doc.message;
-
-		d.hide();
-
-		frappe.call('erpnext.e_commerce.shopping_cart.cart.create_lead_for_item_inquiry', {
-			lead: doc,
-			subject: values.subject,
-			message: values.message
-		}).then(r => {
-			if (r.message) {
-				d.clear();
-			}
-		});
-	}
-
-	$('.btn-inquiry').click((e) => {
-		const $btn = $(e.target);
-		const item_code = $btn.data('item-code');
-		d.set_value('subject', 'Inquiry about ' + item_code);
-		if (!['Administrator', 'Guest'].includes(frappe.session.user)) {
-			d.set_value('email_id', frappe.session.user);
-			d.set_value('lead_name', frappe.get_cookie('full_name'));
-		}
-
-		d.show();
-	});
-});
diff --git a/erpnext/templates/generators/item/item_reviews.html b/erpnext/templates/generators/item/item_reviews.html
deleted file mode 100644
index c62c6f7..0000000
--- a/erpnext/templates/generators/item/item_reviews.html
+++ /dev/null
@@ -1,88 +0,0 @@
-{% from "erpnext/templates/includes/macros.html" import user_review, ratings_summary %}
-
-<div class="mt-4 ratings-reviews-section">
-		<!-- Title and Action -->
-		<div class="w-100 mt-4 mb-2 d-flex">
-			<div class="reviews-header col-9">
-				{{ _("Customer Reviews") }}
-			</div>
-
-			<div class="write-a-review-btn col-3">
-				<!-- Write a Review for legitimate users -->
-				{% if frappe.session.user != "Guest" and user_is_customer %}
-					<button class="btn btn-write-review"
-						data-web-item="{{ doc.name }}">
-						{{ _("Write a Review") }}
-					</button>
-				{% endif %}
-			</div>
-		</div>
-
-		<!-- Summary -->
-		{{ ratings_summary(reviews, reviews_per_rating, average_rating, average_whole_rating, for_summary=True, total_reviews=total_reviews) }}
-
-
-	<!-- Reviews and Comments -->
-	<div class="mt-8">
-		{% if reviews %}
-			{{ user_review(reviews) }}
-
-			{% if total_reviews > 4 %}
-				<div class="mt-6 mb-6"style="color: var(--primary);">
-					<a href="/customer_reviews?web_item={{ doc.name }}">{{ _("View all reviews") }}</a>
-				</div>
-			{% endif %}
-
-		{% else %}
-			<h6 class="text-muted mt-6">
-				{{ _("No Reviews") }}
-			</h6>
-		{% endif %}
-	</div>
-</div>
-
-<script>
-	frappe.ready(() => {
-		$('.page_content').on('click', '.btn-write-review', (e) => {
-			// Bind action on write a review button
-			const $btn = $(e.currentTarget);
-
-			let d = new frappe.ui.Dialog({
-				title: __("Write a Review"),
-				fields: [
-					{fieldname: "title", fieldtype: "Data", label: "Headline", reqd: 1},
-					{fieldname: "rating", fieldtype: "Rating", label: "Overall Rating", reqd: 1},
-					{fieldtype: "Section Break"},
-					{fieldname: "comment", fieldtype: "Small Text", label: "Your Review"}
-				],
-				primary_action: function() {
-					var data = d.get_values();
-					frappe.call({
-						method: "erpnext.e_commerce.doctype.item_review.item_review.add_item_review",
-						args: {
-							web_item: "{{ doc.name }}",
-							title: data.title,
-							rating: data.rating,
-							comment: data.comment
-						},
-						freeze: true,
-						freeze_message: __("Submitting Review ..."),
-						callback: function(r) {
-							if(!r.exc) {
-								frappe.msgprint({
-									message: __("Thank you for the review"),
-									title: __("Review Submitted"),
-									indicator: "green"
-								});
-								d.hide();
-								location.reload();
-							}
-						}
-					});
-				},
-				primary_action_label: __('Submit')
-			});
-			d.show();
-		});
-	});
-</script>
diff --git a/erpnext/templates/generators/item/item_specifications.html b/erpnext/templates/generators/item/item_specifications.html
deleted file mode 100644
index 0814d81..0000000
--- a/erpnext/templates/generators/item/item_specifications.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!-- Is reused to render within tabs as well as independently -->
-{% if website_specifications %}
-<div class="{{ 'mt-2' if not show_tabs else 'mt-5'}} item-website-specification">
-	<div class="col-md-11">
-		{% if not show_tabs %}
-			<div class="product-title mb-5 mt-4">
-				Product Details
-			</div>
-		{% endif %}
-		<table class="table">
-		{% for d in website_specifications -%}
-			<tr>
-				<td class="spec-label">{{ d.label }}</td>
-				<td class="spec-content">{{ d.description }}</td>
-			</tr>
-		{%- endfor %}
-		</table>
-	</div>
-</div>
-{% endif %}
diff --git a/erpnext/templates/generators/item_group.html b/erpnext/templates/generators/item_group.html
deleted file mode 100644
index 956c3c51..0000000
--- a/erpnext/templates/generators/item_group.html
+++ /dev/null
@@ -1,72 +0,0 @@
-{% from "erpnext/templates/includes/macros.html" import field_filter_section, attribute_filter_section, discount_range_filters %}
-{% extends "templates/web.html" %}
-
-{% block header %}
-<div class="mb-6">{{ _(item_group_name) }}</div>
-{% endblock header %}
-
-{% block script %}
-<script type="text/javascript" src="/all-products/index.js"></script>
-{% endblock %}
-
-{% block breadcrumbs %}
-<div class="item-breadcrumbs small text-muted">
-	{% include "templates/includes/breadcrumbs.html" %}
-</div>
-{% endblock %}
-
-{% block page_content %}
-<div class="item-group-content" itemscope itemtype="http://schema.org/Product"
-	data-item-group="{{ name }}">
-	<div class="item-group-slideshow">
-		{% if slideshow %} <!-- slideshow -->
-			{{ web_block(
-				"Hero Slider",
-				values=slideshow,
-				add_container=0,
-				add_top_padding=0,
-				add_bottom_padding=0,
-			) }}
-		{% endif %}
-
-		{% if description %} <!-- description -->
-		<div class="item-group-description text-muted mb-5" itemprop="description">{{ description or ""}}</div>
-		{% endif %}
-	</div>
-	<div class="row">
-		<div id="product-listing" class="col-12 order-2 col-md-9 order-md-2 item-card-group-section">
-			<!-- Products Rendered in all-products/index.js-->
-		</div>
-
-		<div class="col-12 order-1 col-md-3 order-md-1">
-			<div class="collapse d-md-block mr-4 filters-section" id="product-filters">
-				<div class="d-flex justify-content-between align-items-center mb-5 title-section">
-					<div class="mb-4 filters-title" > {{ _('Filters') }} </div>
-					<a class="mb-4 clear-filters" href="/{{ doc.route }}">{{ _('Clear All') }}</a>
-				</div>
-				<!-- field filters -->
-				{{ field_filter_section(field_filters) }}
-
-				<!-- attribute filters -->
-				{{ attribute_filter_section(attribute_filters) }}
-
-			</div>
-
-		</div>
-	</div>
-</div>
-
-<script>
-	frappe.ready(() => {
-		$('.btn-prev, .btn-next').click((e) => {
-			const $btn = $(e.target);
-			$btn.prop('disabled', true);
-			const start = $btn.data('start');
-			let query_params = frappe.utils.get_query_params();
-			query_params.start = start;
-			let path = window.location.pathname + '?' + frappe.utils.get_url_from_dict(query_params);
-			window.location.href = path;
-		});
-	});
-</script>
-{% endblock %}
diff --git a/erpnext/templates/includes/cart/address_card.html b/erpnext/templates/includes/cart/address_card.html
deleted file mode 100644
index 830ed64..0000000
--- a/erpnext/templates/includes/cart/address_card.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<div class="card address-card h-100">
-	<div class="btn btn-sm btn-default btn-change-address font-md" style="position: absolute; right: 0; top: 0;">
-		{{ _('Change') }}
-	</div>
-	<div class="card-body p-0">
-		<div class="card-title">{{ address.title }}</div>
-		<div class="card-text mb-2">
-			{{ address.display }}
-		</div>
-		<a href="/addresses?name={{address.name}}" class="card-link">
-			<svg class="icon icon-sm">
-				<use href="#icon-edit"></use>
-			</svg>
-			{{ _('Edit') }}
-		</a>
-	</div>
-</div>
diff --git a/erpnext/templates/includes/cart/address_picker_card.html b/erpnext/templates/includes/cart/address_picker_card.html
deleted file mode 100644
index 646210e..0000000
--- a/erpnext/templates/includes/cart/address_picker_card.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<div class="card address-card h-100">
-	<div class="check" style="position: absolute; right: 15px; top: 15px;">
-		<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check"><polyline points="20 6 9 17 4 12"></polyline></svg>
-	</div>
-	<div class="card-body">
-		<h5 class="card-title">{{ address.title }}</h5>
-		<p class="card-text text-muted">
-			{{ address.display }}
-		</p>
-		<a href="/addresses?name={{address.name}}" class="card-link">{{ _('Edit') }}</a>
-	</div>
-</div>
diff --git a/erpnext/templates/includes/cart/cart_address.html b/erpnext/templates/includes/cart/cart_address.html
deleted file mode 100644
index a8188ec..0000000
--- a/erpnext/templates/includes/cart/cart_address.html
+++ /dev/null
@@ -1,189 +0,0 @@
-{% from "erpnext/templates/includes/cart/cart_macros.html" import show_address %}
-
-{% if addresses | length == 1%}
-	{% set select_address = True %}
-{% endif %}
-
-<div class="mb-3 frappe-card p-5" data-section="shipping-address">
-	<div class="d-flex">
-		<div class="col-6 address-header"><h6>{{ _("Shipping Address") }}</h6></div>
-		<div class="col-6" style="padding: 0;">
-			<a class="ml-4 btn-new-address" role="button">{{ _("Add a new address") }}</a>
-		</div>
-	</div>
-
-	<hr>
-	{% for address in shipping_addresses %}
-	{% if doc.shipping_address_name == address.name %}
-	<div class="row no-gutters" data-fieldname="shipping_address_name">
-		<div class="w-100 address-container" data-address-name="{{address.name}}" data-address-type="shipping" data-active>
-			{% include "templates/includes/cart/address_card.html" %}
-		</div>
-	</div>
-	{% endif %}
-	{% endfor %}
-</div>
-
-<!-- Billing Address -->
-<div class="checkbox ml-1 mb-2">
-	<label for="input_same_billing">
-		<input type="checkbox" class="product-filter" id="input_same_billing" checked style="width: 14px !important">
-		<span class="label-area font-md">{{ _('Billing Address is same as Shipping Address') }}</span>
-	</label>
-</div>
-
-{% if billing_addresses %}
-	<div class="mb-3 frappe-card p-5" data-section="billing-address">
-		<div class="d-flex">
-			<div class="col-6 address-header"><h6>{{ _("Billing Address") }}</h6></div>
-			<div class="col-6" style="padding: 0;">
-				<a class="ml-4 btn-new-address" role="button">{{ _("Add a new address") }}</a>
-			</div>
-		</div>
-
-		<hr>
-		{% for address in billing_addresses %}
-			{% if doc.customer_address == address.name %}
-			<div class="row no-gutters" data-fieldname="customer_address">
-				<div class="w-100 address-container" data-address-name="{{address.name}}" data-address-type="billing" data-active>
-						{% include "templates/includes/cart/address_card.html" %}
-					</div>
-			</div>
-			{% endif %}
-		{% endfor %}
-	</div>
-{% endif %}
-
-<script>
-frappe.boot = {{ boot }}
-frappe.ready(() => {
-	$(document).on('click', '.address-card', (e) => {
-		const $target = $(e.currentTarget);
-		const $section = $target.closest('[data-section]');
-		$section.find('.address-card').removeClass('active');
-		$target.addClass('active');
-	});
-
-	$('#input_same_billing').change((e) => {
-		const $check = $(e.target);
-		toggle_billing_address_section(!$check.is(':checked'));
-	});
-
-	$('.btn-new-address').click(() => {
-		const d = new frappe.ui.Dialog({
-			title: __('New Address'),
-			fields: [
-				{
-					label: __('Address Title'),
-					fieldname: 'address_title',
-					fieldtype: 'Data',
-					reqd: 1
-				},
-				{
-					label: __('Address Line 1'),
-					fieldname: 'address_line1',
-					fieldtype: 'Data',
-					reqd: 1
-				},
-				{
-					label: __('Address Line 2'),
-					fieldname: 'address_line2',
-					fieldtype: 'Data'
-				},
-				{
-					label: __('City/Town'),
-					fieldname: 'city',
-					fieldtype: 'Data',
-					reqd: 1
-				},
-				{
-					label: __('State'),
-					fieldname: 'state',
-					fieldtype: 'Data'
-				},
-				{
-					label: __('Country'),
-					fieldname: 'country',
-					fieldtype: 'Link',
-					options: 'Country',
-					only_select: true,
-					reqd: 1
-				},
-				{
-					fieldname: "column_break0",
-					fieldtype: "Column Break",
-					width: "50%"
-				},
-				{
-					label: __('Address Type'),
-					fieldname: 'address_type',
-					fieldtype: 'Select',
-					options: [
-						'Billing',
-						'Shipping'
-					],
-					reqd: 1
-				},
-				{
-					label: __('Postal Code'),
-					fieldname: 'pincode',
-					fieldtype: 'Data'
-				},
-				{
-					fieldname: "phone",
-					fieldtype: "Data",
-					label: "Phone",
-					reqd: 1
-				},
-			],
-			primary_action_label: __('Save'),
-			primary_action: (values) => {
-				frappe.call('erpnext.e_commerce.shopping_cart.cart.add_new_address', { doc: values })
-					.then(r => {
-						frappe.call({
-							method: "erpnext.e_commerce.shopping_cart.cart.update_cart_address",
-							args: {
-								address_type: r.message.address_type,
-								address_name: r.message.name
-							},
-							callback: function (r) {
-								d.hide();
-								window.location.reload();
-							}
-						});
-					});
-
-			}
-		})
-
-		d.show();
-	});
-
-	function setup_state() {
-		const shipping_address = $('[data-section="shipping-address"]')
-			.find('[data-address-name][data-active]').attr('data-address-name');
-
-		const billing_address = $('[data-section="billing-address"]')
-			.find('[data-address-name][data-active]').attr('data-address-name');
-
-		$('#input_same_billing').prop('checked', shipping_address === billing_address).trigger('change');
-
-		if (!shipping_address && !billing_address) {
-			$('#input_same_billing').prop('checked', true).trigger('change');
-		}
-
-		if (shipping_address) {
-			$(`[data-section="shipping-address"] [data-address-name="${shipping_address}"] .address-card`).addClass('active');
-		}
-		if (billing_address) {
-			$(`[data-section="billing-address"] [data-address-name="${billing_address}"] .address-card`).addClass('active');
-		}
-	}
-
-	setup_state();
-
-	function toggle_billing_address_section(flag) {
-		$('[data-section="billing-address"]').toggle(flag);
-	}
-});
-</script>
diff --git a/erpnext/templates/includes/cart/cart_address_picker.html b/erpnext/templates/includes/cart/cart_address_picker.html
deleted file mode 100644
index 66a50ec..0000000
--- a/erpnext/templates/includes/cart/cart_address_picker.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<div class="mb-3 frappe-card p-5" data-section="shipping-address">
-	<h6>{{ _("Shipping Address") }}</h6>
-</div>
diff --git a/erpnext/templates/includes/cart/cart_dropdown.html b/erpnext/templates/includes/cart/cart_dropdown.html
deleted file mode 100644
index 38ad183..0000000
--- a/erpnext/templates/includes/cart/cart_dropdown.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<div class="cart-dropdown-container">
-	<div id="cart-error" class="alert alert-danger"
-		style="display: none;"></div>
-	<div class="row checkout-btn">
-		<div class="col-sm-12 col-xs-12">
-			<a href="/cart" class="btn btn-block btn-primary">{{ _("Checkout") }}</a>
-		</div>
-	</div>
-	<div class="row cart-items-dropdown cart-item-header text-muted">
-		<div class="col-sm-6 col-xs-6 h6 text-uppercase">
-		{{ _("Item") }}
-		</div>
-		<div class="col-sm-6 col-xs-6 text-right h6 text-uppercase">
-		{{ _("Price") }}
-		</div>
-	</div>
-
-	{% if doc.items %}
-	<div class="row cart-items-dropdown">
-		<div class="col-sm-12 col-xs-12">
-			{% include "templates/includes/cart/cart_items_dropdown.html" %}
-		</div>
-	</div>
-	{% else %}
-	<p>{{ _("Cart is Empty") }}</p>
-	{% endif %}
-</div>
diff --git a/erpnext/templates/includes/cart/cart_items.html b/erpnext/templates/includes/cart/cart_items.html
deleted file mode 100644
index 428b36e..0000000
--- a/erpnext/templates/includes/cart/cart_items.html
+++ /dev/null
@@ -1,113 +0,0 @@
-{% from "erpnext/templates/includes/macros.html" import product_image %}
-
-{% macro item_subtotal(item) %}
-	<div>
-		{{ item.get_formatted('amount') }}
-	</div>
-
-	{% if item.is_free_item %}
-		<div class="text-success mt-4">
-			<span class="free-tag">
-				{{ _('FREE') }}
-			</span>
-		</div>
-	{% else %}
-		<span class="item-rate">
-			{{ _('Rate:') }} {{ item.get_formatted('rate') }}
-		</span>
-	{% endif %}
-{% endmacro %}
-
-{% for d in doc.items %}
-	<tr data-name="{{ d.name }}">
-		<td style="width: 60%;">
-			<div class="d-flex">
-				<div class="cart-item-image mr-4">
-					{% if d.thumbnail %}
-						{{ product_image(d.thumbnail, alt="d.web_item_name", no_border=True) }}
-					{% else %}
-						<div class = "no-image-cart-item">
-							{{ frappe.utils.get_abbr(d.web_item_name) or "NA" }}
-						</div>
-					{% endif %}
-				</div>
-
-				<div class="d-flex w-100" style="flex-direction: column;">
-					<div class="item-title mb-1 mr-3">
-						{{ d.get("web_item_name") or d.item_name }}
-					</div>
-					<div class="item-subtitle mr-2">
-						{{ d.item_code }}
-					</div>
-					{%- set variant_of = frappe.db.get_value('Item', d.item_code, 'variant_of') %}
-					{% if variant_of %}
-					<span class="item-subtitle mr-2">
-						{{ _('Variant of') }}
-						<a href="{{frappe.db.get_value('Website Item', {'item_code': variant_of}, 'route') or '#'}}">
-							{{ variant_of }}
-						</a>
-					</span>
-					{% endif %}
-
-					<div class="mt-2 notes">
-						<textarea data-item-code="{{d.item_code}}" class="form-control" rows="2" placeholder="{{ _('Add notes') }}">
-							{{d.additional_notes or ''}}
-						</textarea>
-					</div>
-				</div>
-			</div>
-		</td>
-
-		<!-- Qty column -->
-		<td class="text-right" style="width: 25%;">
-			<div class="d-flex">
-				{% set disabled = 'disabled' if d.is_free_item else '' %}
-				<div class="input-group number-spinner mt-1 mb-4">
-					<span class="input-group-prepend d-sm-inline-block">
-						<button class="btn cart-btn" data-dir="dwn" {{ disabled }}>
-							{{ '–' if not d.is_free_item else ''}}
-						</button>
-					</span>
-
-					<input class="form-control text-center cart-qty" value="{{ d.get_formatted('qty') }}" data-item-code="{{ d.item_code }}"
-						style="max-width: 70px;" {{ disabled }}>
-
-					<span class="input-group-append d-sm-inline-block">
-						<button class="btn cart-btn" data-dir="up" {{ disabled }}>
-							{{ '+' if not d.is_free_item else ''}}
-						</button>
-					</span>
-					</div>
-
-				<div>
-					{% if not d.is_free_item %}
-						<div class="remove-cart-item column-sm-view d-flex" data-item-code="{{ d.item_code }}">
-							<span>
-								<svg class="icon sm remove-cart-item-logo"
-									width="18" height="18" viewBox="0 0 18 18"
-									xmlns="http://www.w3.org/2000/svg" id="icon-close">
-									<path fill-rule="evenodd" clip-rule="evenodd" d="M4.146 11.217a.5.5 0 1 0 .708.708l3.182-3.182 3.181 3.182a.5.5 0 1 0 .708-.708l-3.182-3.18 3.182-3.182a.5.5 0 1 0-.708-.708l-3.18 3.181-3.183-3.182a.5.5 0 0 0-.708.708l3.182 3.182-3.182 3.181z" stroke-width="0"></path>
-								</svg>
-							</span>
-						</div>
-					{% endif %}
-					</div>
-			</div>
-
-
-			<!-- Shown on mobile view, else hidden -->
-			{% if cart_settings.enable_checkout or cart_settings.show_price_in_quotation %}
-				<div class="text-right sm-item-subtotal">
-					{{ item_subtotal(d) }}
-				</div>
-			{% endif %}
-		</td>
-
-		<!-- Subtotal column -->
-		{% if cart_settings.enable_checkout or cart_settings.show_price_in_quotation %}
-			<td class="text-right item-subtotal column-sm-view w-100">
-				{{ item_subtotal(d) }}
-			</td>
-		{% endif %}
-	</tr>
-{% endfor %}
diff --git a/erpnext/templates/includes/cart/cart_items_dropdown.html b/erpnext/templates/includes/cart/cart_items_dropdown.html
deleted file mode 100644
index 5d107fc..0000000
--- a/erpnext/templates/includes/cart/cart_items_dropdown.html
+++ /dev/null
@@ -1,12 +0,0 @@
-{% from "erpnext/templates/includes/order/order_macros.html" import item_name_and_description_cart %}
-
-{% for d in doc.items %}
-<div class="row cart-dropdown">
-    <div class="col-sm-8 col-xs-8 col-name-description">
-        {{ item_name_and_description_cart(d) }}
-    </div>
-    <div class="col-sm-4 col-xs-4 text-right col-amount">
-        {{ d.get_formatted("amount") }}
-    </div>
-</div>
-{% endfor %}
diff --git a/erpnext/templates/includes/cart/cart_items_total.html b/erpnext/templates/includes/cart/cart_items_total.html
deleted file mode 100644
index c94fde4..0000000
--- a/erpnext/templates/includes/cart/cart_items_total.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!-- Total at the end of the cart items -->
-<tr>
-	<th></th>
-	<th class="text-left item-grand-total" colspan="1">
-		{{ _("Total") }}
-	</th>
-	<th class="text-left item-grand-total totals" colspan="3">
-		{{ doc.get_formatted("total") }}
-	</th>
-</tr>
\ No newline at end of file
diff --git a/erpnext/templates/includes/cart/cart_macros.html b/erpnext/templates/includes/cart/cart_macros.html
deleted file mode 100644
index fd95dba..0000000
--- a/erpnext/templates/includes/cart/cart_macros.html
+++ /dev/null
@@ -1,22 +0,0 @@
-{% macro show_address(address, doc, fieldname, select_address=False) %}
-{% set selected=address.name==doc.get(fieldname) %}
-
-<div class="panel panel-default">
-	<div class="panel-heading">
-		<div class="row">
-			<div class="col-sm-10 address-title"
-				data-address-name="{{ address.name }}">
-                <strong>{{ address.name }}</strong></div>
-			<div class="col-sm-2 text-right">
-                <input type="checkbox"
-                data-fieldname="{{ fieldname }}"
-				data-address-name="{{ address.name}}"
-                    {{ "checked" if selected else "" }}></div>
-		</div>
-	</div>
-	<div class="panel-collapse"
-        data-address-name="{{ address.name }}">
-		<div class="panel-body text-muted small">{{ address.display }}</div>
-	</div>
-</div>
-{% endmacro %}
diff --git a/erpnext/templates/includes/cart/cart_payment_summary.html b/erpnext/templates/includes/cart/cart_payment_summary.html
deleted file mode 100644
index b5655a2..0000000
--- a/erpnext/templates/includes/cart/cart_payment_summary.html
+++ /dev/null
@@ -1,84 +0,0 @@
-<!-- Payment -->
-{% if cart_settings.enable_checkout or cart_settings.show_price_in_quotation %}
-<h6>
-	{{ _("Payment Summary") }}
-</h6>
-{% endif %}
-
-<div class="card h-100">
-	<div class="card-body p-0">
-		{% if cart_settings.enable_checkout or cart_settings.show_price_in_quotation %}
-			<table class="table w-100">
-				<tr>
-					{% set total_items = frappe.utils.cstr(frappe.utils.flt(doc.total_qty, 0)) %}
-					<td class="bill-label">{{ _("Net Total (") + total_items + _(" Items)") }}</td>
-					<td class="bill-content net-total text-right">{{ doc.get_formatted("net_total") }}</td>
-				</tr>
-
-				<!-- taxes -->
-				{% for d in doc.taxes %}
-					{% if d.base_tax_amount %}
-						<tr>
-							<td class="bill-label">
-								{{ d.description }}
-							</td>
-							<td class="bill-content text-right">
-								{{ d.get_formatted("base_tax_amount") }}
-							</td>
-						</tr>
-					{% endif %}
-				{% endfor %}
-			</table>
-
-			<!-- TODO: Apply Coupon Dialog-->
-			<!-- {% set show_coupon_code = cart_settings.show_apply_coupon_code_in_website and cart_settings.enable_checkout %}
-			{% if show_coupon_code %}
-				<button class="btn btn-coupon-code w-100 text-left">
-					<svg width="24" height="24" viewBox="0 0 24 24" stroke="var(--gray-600)" fill="none" xmlns="http://www.w3.org/2000/svg">
-						<path d="M19 15.6213C19 15.2235 19.158 14.842 19.4393 14.5607L20.9393 13.0607C21.5251 12.4749 21.5251 11.5251 20.9393 10.9393L19.4393 9.43934C19.158 9.15804 19 8.7765 19 8.37868V6.5C19 5.67157 18.3284 5 17.5 5H15.6213C15.2235 5 14.842 4.84196 14.5607 4.56066L13.0607 3.06066C12.4749 2.47487 11.5251 2.47487 10.9393 3.06066L9.43934 4.56066C9.15804 4.84196 8.7765 5 8.37868 5H6.5C5.67157 5 5 5.67157 5 6.5V8.37868C5 8.7765 4.84196 9.15804 4.56066 9.43934L3.06066 10.9393C2.47487 11.5251 2.47487 12.4749 3.06066 13.0607L4.56066 14.5607C4.84196 14.842 5 15.2235 5 15.6213V17.5C5 18.3284 5.67157 19 6.5 19H8.37868C8.7765 19 9.15804 19.158 9.43934 19.4393L10.9393 20.9393C11.5251 21.5251 12.4749 21.5251 13.0607 20.9393L14.5607 19.4393C14.842 19.158 15.2235 19 15.6213 19H17.5C18.3284 19 19 18.3284 19 17.5V15.6213Z" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
-						<path d="M15 9L9 15" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
-						<path d="M10.5 9.5C10.5 10.0523 10.0523 10.5 9.5 10.5C8.94772 10.5 8.5 10.0523 8.5 9.5C8.5 8.94772 8.94772 8.5 9.5 8.5C10.0523 8.5 10.5 8.94772 10.5 9.5Z" fill="white" stroke-linecap="round" stroke-linejoin="round"/>
-						<path d="M15.5 14.5C15.5 15.0523 15.0523 15.5 14.5 15.5C13.9477 15.5 13.5 15.0523 13.5 14.5C13.5 13.9477 13.9477 13.5 14.5 13.5C15.0523 13.5 15.5 13.9477 15.5 14.5Z" fill="white" stroke-linecap="round" stroke-linejoin="round"/>
-					</svg>
-					<span class="ml-2">Apply Coupon</span>
-				</button>
-			{% endif %} -->
-
-			<table class="table w-100 grand-total mt-6">
-				<tr>
-					<td class="bill-content net-total">{{ _("Grand Total") }}</td>
-					<td class="bill-content net-total text-right">{{ doc.get_formatted("grand_total") }}</td>
-				</tr>
-			</table>
-		{% endif %}
-
-		{% if cart_settings.enable_checkout %}
-			<button class="btn btn-primary btn-place-order font-md w-100" type="button">
-				{{ _('Place Order') }}
-			</button>
-		{% else %}
-			<button class="btn btn-primary btn-request-for-quotation font-md w-100" type="button">
-				{{ _('Request for Quote') }}
-			</button>
-		{% endif %}
-	</div>
-</div>
-
-<!-- TODO: Apply Coupon Dialog-->
-<!-- <script>
-	frappe.ready(() => {
-		$('.btn-coupon-code').click((e) => {
-			const $btn = $(e.currentTarget);
-			const d = new frappe.ui.Dialog({
-				title: __('Coupons'),
-				fields: [
-					{
-						fieldname: 'coupons_area',
-						fieldtype: 'HTML'
-					}
-				]
-			});
-			d.show();
-		});
-	});
-</script> -->
\ No newline at end of file
diff --git a/erpnext/templates/includes/integrations/gocardless_checkout.js b/erpnext/templates/includes/integrations/gocardless_checkout.js
deleted file mode 100644
index b18d550..0000000
--- a/erpnext/templates/includes/integrations/gocardless_checkout.js
+++ /dev/null
@@ -1,24 +0,0 @@
-$(document).ready(function() {
-	var data = {{ frappe.form_dict | json }};
-	var doctype = "{{ reference_doctype }}"
-	var docname = "{{ reference_docname }}"
-
-	frappe.call({
-		method: "erpnext.templates.pages.integrations.gocardless_checkout.check_mandate",
-		freeze: true,
-		headers: {
-			"X-Requested-With": "XMLHttpRequest"
-		},
-		args: {
-			"data": JSON.stringify(data),
-			"reference_doctype": doctype,
-			"reference_docname": docname
-		},
-		callback: function(r) {
-			if (r.message) {
-				window.location.href = r.message.redirect_to
-			}
-		}
-	})
-
-})
diff --git a/erpnext/templates/includes/integrations/gocardless_confirmation.js b/erpnext/templates/includes/integrations/gocardless_confirmation.js
deleted file mode 100644
index fee1d2b..0000000
--- a/erpnext/templates/includes/integrations/gocardless_confirmation.js
+++ /dev/null
@@ -1,24 +0,0 @@
-$(document).ready(function() {
-	var redirect_flow_id = "{{ redirect_flow_id }}";
-	var doctype = "{{ reference_doctype }}";
-	var docname = "{{ reference_docname }}";
-
-	frappe.call({
-		method: "erpnext.templates.pages.integrations.gocardless_confirmation.confirm_payment",
-		freeze: true,
-		headers: {
-			"X-Requested-With": "XMLHttpRequest"
-		},
-		args: {
-			"redirect_flow_id": redirect_flow_id,
-			"reference_doctype": doctype,
-			"reference_docname": docname
-		},
-		callback: function(r) {
-			if (r.message) {
-				window.location.href = r.message.redirect_to;
-			}
-		}
-	});
-
-});
diff --git a/erpnext/templates/includes/navbar/navbar_items.html b/erpnext/templates/includes/navbar/navbar_items.html
deleted file mode 100644
index d7adae5..0000000
--- a/erpnext/templates/includes/navbar/navbar_items.html
+++ /dev/null
@@ -1,22 +0,0 @@
-{% extends 'frappe/templates/includes/navbar/navbar_items.html' %}
-
-{% block navbar_right_extension %}
-	<li class="shopping-cart cart-icon hidden">
-		<a class="nav-link" href="/cart">
-			<svg class="icon icon-lg">
-				<use href="#icon-assets"></use>
-			</svg>
-			<span class="badge badge-primary shopping-badge" id="cart-count"></span>
-		</a>
-	</li>
-	{% if frappe.db.get_single_value("E Commerce Settings", "enable_wishlist") %}
-		<li class="wishlist wishlist-icon hidden">
-			<a class="nav-link" href="/wishlist">
-				<svg class="icon icon-lg">
-					<use href="#icon-heart"></use>
-				</svg>
-				<span class="badge badge-primary shopping-badge" id="wish-count"></span>
-			</a>
-		</li>
-	{% endif %}
-{% endblock %}
diff --git a/erpnext/templates/includes/order/order_macros.html b/erpnext/templates/includes/order/order_macros.html
deleted file mode 100644
index d95b289..0000000
--- a/erpnext/templates/includes/order/order_macros.html
+++ /dev/null
@@ -1,52 +0,0 @@
-{% from "erpnext/templates/includes/macros.html" import product_image %}
-
-{% macro item_name_and_description(d) %}
-	<div class="row item_name_and_description">
-		<div class="col-xs-4 col-sm-2 order-image-col">
-			<div class="order-image h-100">
-				{% if d.thumbnail or d.image %}
-					{{ product_image(d.thumbnail or d.image, no_border=True) }}
-				{% else %}
-					<div class="no-image-cart-item" style="min-height: 100px;">
-						{{ frappe.utils.get_abbr(d.item_name) or "NA" }}
-					</div>
-				{% endif %}
-			</div>
-		</div>
-		<div class="col-xs-8 col-sm-10">
-			{{ d.item_code }}
-			<div class="text-muted small item-description">
-				{{ html2text(d.description) | truncate(140) }}
-			</div>
-			<span class="text-muted mt-2 d-l-n order-qty">
-				{{ _("Qty ") }}({{ d.get_formatted("qty") }})
-			</span>
-		</div>
-	</div>
-{% endmacro %}
-
-{% macro item_name_and_description_cart(d) %}
-	<div class="row item_name_dropdown">
-		<div class="col-xs-4 col-sm-4 order-image-col">
-			<div class="order-image">
-			 {{ product_image_square(d.thumbnail or d.image) }}
-			</div>
-		</div>
-		<div class="col-xs-8 col-sm-8">
-		   {{ d.item_name|truncate(25) }}
-			<div class="input-group number-spinner">
-				<span class="input-group-btn">
-					<button class="btn btn-light cart-btn" data-dir="dwn">
-						–</button>
-				</span>
-				<input class="form-control text-right cart-qty"
-					value = "{{ d.get_formatted('qty') }}"
-					data-item-code="{{ d.item_code }}">
-				<span class="input-group-btn">
-					<button class="btn btn-light cart-btn" data-dir="up">
-						+</button>
-				</span>
-			</div>
-		</div>
-	</div>
-{% endmacro %}
diff --git a/erpnext/templates/includes/product_page.js b/erpnext/templates/includes/product_page.js
deleted file mode 100644
index a3979d0..0000000
--- a/erpnext/templates/includes/product_page.js
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-// License: GNU General Public License v3. See license.txt
-
-frappe.ready(function() {
-	window.item_code = $('[itemscope] [itemprop="productID"]').text().trim();
-	var qty = 0;
-
-	frappe.call({
-		type: "POST",
-		method: "erpnext.e_commerce.shopping_cart.product_info.get_product_info_for_website",
-		args: {
-			item_code: get_item_code()
-		},
-		callback: function(r) {
-			if(r.message) {
-				if(r.message.cart_settings.enabled) {
-					let hide_add_to_cart = !r.message.product_info.price
-						|| (!r.message.product_info.in_stock && !r.message.cart_settings.allow_items_not_in_stock);
-					$(".item-cart, .item-price, .item-stock").toggleClass('hide', hide_add_to_cart);
-				}
-				if(r.message.cart_settings.show_price) {
-					$(".item-price").toggleClass("hide", false);
-				}
-				if(r.message.cart_settings.show_stock_availability) {
-					$(".item-stock").toggleClass("hide", false);
-				}
-				if(r.message.product_info.price) {
-					$(".item-price")
-						.html(r.message.product_info.price.formatted_price_sales_uom + "<div style='font-size: small'>\
-							(" + r.message.product_info.price.formatted_price + " / " + r.message.product_info.uom + ")</div>");
-
-					if(r.message.product_info.in_stock===0) {
-						$(".item-stock").html("<div style='color: red'> <i class='fa fa-close'></i> {{ _("Not in stock") }}</div>");
-					}
-					else if(r.message.product_info.in_stock===1 && r.message.cart_settings.show_stock_availability) {
-						var qty_display = "{{ _("In stock") }}";
-						if (r.message.product_info.show_stock_qty) {
-							qty_display += " ("+r.message.product_info.stock_qty+")";
-						}
-						$(".item-stock").html("<div style='color: green'>\
-							<i class='fa fa-check'></i> "+qty_display+"</div>");
-					}
-
-					if(r.message.product_info.qty) {
-						qty = r.message.product_info.qty;
-						toggle_update_cart(r.message.product_info.qty);
-					} else {
-						toggle_update_cart(0);
-					}
-				}
-			}
-		}
-	})
-
-	$("#item-add-to-cart button").on("click", function() {
-		frappe.provide('erpnext.shopping_cart');
-
-		erpnext.shopping_cart.update_cart({
-			item_code: get_item_code(),
-			qty: $("#item-spinner .cart-qty").val(),
-			callback: function(r) {
-				if(!r.exc) {
-					toggle_update_cart(1);
-					qty = 1;
-				}
-			},
-			btn: this,
-		});
-	});
-
-	$("#item-spinner").on('click', '.number-spinner button', function () {
-		var btn = $(this),
-			input = btn.closest('.number-spinner').find('input'),
-			oldValue = input.val().trim(),
-			newVal = 0;
-
-		if (btn.attr('data-dir') == 'up') {
-			newVal = Number.parseInt(oldValue) + 1;
-		} else if (btn.attr('data-dir') == 'dwn')  {
-			if (Number.parseInt(oldValue) > 1) {
-				newVal = Number.parseInt(oldValue) - 1;
-			}
-			else {
-				newVal = Number.parseInt(oldValue);
-			}
-		}
-		input.val(newVal);
-	});
-
-	$("[itemscope] .item-view-attribute .form-control").on("change", function() {
-		try {
-			var item_code = encodeURIComponent(get_item_code());
-
-		} catch(e) {
-			// unable to find variant
-			// then chose the closest available one
-
-			var attribute = $(this).attr("data-attribute");
-			var attribute_value = $(this).val();
-			var item_code = find_closest_match(attribute, attribute_value);
-
-			if (!item_code) {
-				frappe.msgprint(__("Cannot find a matching Item. Please select some other value for {0}.", [attribute]))
-				throw e;
-			}
-		}
-
-		if (window.location.search == ("?variant=" + item_code) || window.location.search.includes(item_code)) {
-			return;
-		}
-
-		window.location.href = window.location.pathname + "?variant=" + item_code;
-	});
-
-	// change the item image src when alternate images are hovered
-	$(document.body).on('mouseover', '.item-alternative-image', (e) => {
-		const $alternative_image = $(e.currentTarget);
-		const src = $alternative_image.find('img').prop('src');
-		$('.item-image img').prop('src', src);
-	});
-});
-
-var toggle_update_cart = function(qty) {
-	$("#item-add-to-cart").toggle(qty ? false : true);
-	$("#item-update-cart")
-		.toggle(qty ? true : false)
-		.find("input").val(qty);
-	$("#item-spinner").toggle(qty ? false : true);
-}
-
-function get_item_code() {
-	var variant_info = window.variant_info;
-	if(variant_info) {
-		var attributes = get_selected_attributes();
-		var no_of_attributes = Object.keys(attributes).length;
-
-		for(var i in variant_info) {
-			var variant = variant_info[i];
-
-			if (variant.attributes.length < no_of_attributes) {
-				// the case when variant has less attributes than template
-				continue;
-			}
-
-			var match = true;
-			for(var j in variant.attributes) {
-				if(attributes[variant.attributes[j].attribute]
-					!= variant.attributes[j].attribute_value
-				) {
-					match = false;
-					break;
-				}
-			}
-			if(match) {
-				return variant.name;
-			}
-		}
-		throw "Unable to match variant";
-	} else {
-		return window.item_code;
-	}
-}
-
-function find_closest_match(selected_attribute, selected_attribute_value) {
-	// find the closest match keeping the selected attribute in focus and get the item code
-
-	var attributes = get_selected_attributes();
-
-	var previous_match_score = 0;
-	var previous_no_of_attributes = 0;
-	var matched;
-
-	var variant_info = window.variant_info;
-	for(var i in variant_info) {
-		var variant = variant_info[i];
-		var match_score = 0;
-		var has_selected_attribute = false;
-
-		for(var j in variant.attributes) {
-			if(attributes[variant.attributes[j].attribute]===variant.attributes[j].attribute_value) {
-				match_score = match_score + 1;
-
-				if (variant.attributes[j].attribute==selected_attribute && variant.attributes[j].attribute_value==selected_attribute_value) {
-					has_selected_attribute = true;
-				}
-			}
-		}
-
-		if (has_selected_attribute
-			&& ((match_score > previous_match_score) || (match_score==previous_match_score && previous_no_of_attributes < variant.attributes.length))) {
-			previous_match_score = match_score;
-			matched = variant;
-			previous_no_of_attributes = variant.attributes.length;
-
-
-		}
-	}
-
-	if (matched) {
-		for (var j in matched.attributes) {
-			var attr = matched.attributes[j];
-			$('[itemscope]')
-				.find(repl('.item-view-attribute .form-control[data-attribute="%(attribute)s"]', attr))
-				.val(attr.attribute_value);
-		}
-
-		return matched.name;
-	}
-}
-
-function get_selected_attributes() {
-	var attributes = {};
-	$('[itemscope]').find(".item-view-attribute .form-control").each(function() {
-		attributes[$(this).attr('data-attribute')] = $(this).val();
-	});
-	return attributes;
-}
diff --git a/erpnext/templates/includes/rfq/rfq_macros.html b/erpnext/templates/includes/rfq/rfq_macros.html
index 88724c3..78ec6ff 100644
--- a/erpnext/templates/includes/rfq/rfq_macros.html
+++ b/erpnext/templates/includes/rfq/rfq_macros.html
@@ -1,19 +1,25 @@
 {% from "erpnext/templates/includes/macros.html" import product_image_square, product_image %}
 
 {% macro item_name_and_description(d, doc) %}
-    <div class="row">
-        <div class="col-3">
-			{{ product_image(d.image) }}
-        </div>
-        <div class="col-9">
-            {{ d.item_code }}
-            <p class="text-muted small">{{ d.description }}</p>
+	<div class="row">
+		<div class="col-3">
+			{% if d.image %}
+				{{ product_image(d.image) }}
+			{% else %}
+				<div class="website-image h-100 w-100" style="background-color:var(--gray-100);text-align: center;line-height: 3.6;">
+					{{ frappe.utils.get_abbr(d.item_name)}}
+				</div>
+			{% endif %}
+		</div>
+		<div class="col-9">
+			{{ d.item_code }}
+			<p class="text-muted small">{{ d.description }}</p>
 			{% set supplier_part_no = frappe.db.get_value("Item Supplier", {'parent': d.item_code, 'supplier': doc.supplier}, "supplier_part_no") %}
 			<p class="text-muted small supplier-part-no">
 				{% if supplier_part_no %}
 					{{_("Supplier Part No") + ": "+ supplier_part_no}}
 				{% endif %}
 			</p>
-        </div>
-    </div>
+		</div>
+	</div>
 {% endmacro %}
diff --git a/erpnext/templates/pages/cart.html b/erpnext/templates/pages/cart.html
deleted file mode 100644
index 2b7d9e3..0000000
--- a/erpnext/templates/pages/cart.html
+++ /dev/null
@@ -1,132 +0,0 @@
-{% extends "templates/web.html" %}
-
-{% block title %} {{ _("Shopping Cart") }} {% endblock %}
-
-{% block header %}<h3 class="shopping-cart-header mt-2 mb-6">{{ _("Shopping Cart") }}</h1>{% endblock %}
-
-{% block header_actions %}
-{% endblock %}
-
-{% block page_content %}
-
-{% from "templates/includes/macros.html" import item_name_and_description %}
-
-{% if doc.items %}
-<div class="cart-container">
-	<div class="row m-0">
-		<!-- Left section -->
-		<div class="col-md-8">
-			<div class="frappe-card p-5 mb-4">
-				<div id="cart-error" class="alert alert-danger" style="display: none;"></div>
-				<div class="cart-items-header">
-					{{ _('Items') }}
-				</div>
-				<table class="table mt-3 cart-table">
-					<thead>
-						<tr>
-							<th class="item-column">{{ _('Item') }}</th>
-							<th width="20%">{{ _('Quantity') }}</th>
-							{% if cart_settings.enable_checkout or cart_settings.show_price_in_quotation %}
-								<th width="20" class="text-right column-sm-view">{{ _('Subtotal') }}</th>
-							{% endif %}
-							<th width="10%" class="column-sm-view"></th>
-						</tr>
-					</thead>
-					<tbody class="cart-items">
-						{% include "templates/includes/cart/cart_items.html" %}
-					</tbody>
-
-					{% if cart_settings.enable_checkout or cart_settings.show_price_in_quotation %}
-						<tfoot class="cart-tax-items">
-							{% include "templates/includes/cart/cart_items_total.html" %}
-						</tfoot>
-					{% endif %}
-				</table>
-
-				<div class="row mt-2">
-					<div class="col-3">
-						{% if cart_settings.enable_checkout %}
-							<a class="btn btn-primary-light font-md" href="/orders">
-								{{ _('Past Orders') }}
-							</a>
-						{% else %}
-							<a class="btn btn-primary-light font-md" href="/quotations">
-								{{ _('Past Quotes') }}
-							</a>
-						{% endif %}
-					</div>
-					<div class="col-9">
-						{% if doc.items %}
-						<div class="place-order-container">
-							<a class="btn btn-primary-light mr-2 font-md" href="/all-products">
-								{{ _('Continue Shopping') }}
-							</a>
-						</div>
-						{% endif %}
-					</div>
-				</div>
-			</div>
-
-			<!-- Terms and Conditions -->
-			{% if doc.items %}
-				{% if doc.terms %}
-					<div class="t-and-c-container mt-4 frappe-card">
-						<h5>{{ _("Terms and Conditions") }}</h5>
-						<div class="t-and-c-terms mt-2">
-							{{ doc.terms }}
-						</div>
-					</div>
-				{% endif %}
-		</div>
-
-		<!-- Right section -->
-		<div class="col-md-4">
-			<div class="cart-payment-addresses">
-				<!-- Apply Coupon Code  -->
-				{% set show_coupon_code = cart_settings.show_apply_coupon_code_in_website and cart_settings.enable_checkout %}
-				{% if show_coupon_code == 1%}
-					<div class="mb-3">
-						<div class="row no-gutters">
-							<input type="text" class="txtcoupon form-control mr-3 w-50 font-md" placeholder="Enter Coupon Code" name="txtcouponcode"  ></input>
-							<button class="btn btn-primary btn-sm bt-coupon font-md">{{ _("Apply Coupon Code") }}</button>
-							<input type="hidden" class="txtreferral_sales_partner font-md" placeholder="Enter Sales Partner" name="txtreferral_sales_partner" type="text"></input>
-							</div>
-					</div>
-				{% endif %}
-
-				<div class="mb-3 frappe-card p-5 payment-summary">
-					{% include "templates/includes/cart/cart_payment_summary.html" %}
-				</div>
-
-				{% include "templates/includes/cart/cart_address.html" %}
-			</div>
-		</div>
-		{% endif %}
-	</div>
-</div>
-{% else %}
-<div class="cart-empty frappe-card">
-	<div class="cart-empty-state">
-		<img src="/assets/erpnext/images/ui-states/cart-empty-state.png" alt="Empty State">
-	</div>
-	<div class="cart-empty-message mt-4">{{ _('Your cart is Empty') }}</p>
-	{% if cart_settings.enable_checkout %}
-		<a class="btn btn-outline-primary" href="/orders" style="font-size: 16px;">
-			{{ _('See past orders') }}
-		</a>
-		{% else %}
-		<a class="btn btn-outline-primary" href="/quotations" style="font-size: 16px;">
-			{{ _('See past quotations') }}
-		</a>
-	{% endif %}
-</div>
-{% endif %}
-
-{% endblock %}
-
-{% block base_scripts %}
-<!-- js should be loaded in body! -->
-{{ include_script("frappe-web.bundle.js") }}
-{{ include_script("controls.bundle.js") }}
-{{ include_script("dialog.bundle.js") }}
-{% endblock %}
diff --git a/erpnext/templates/pages/cart.js b/erpnext/templates/pages/cart.js
deleted file mode 100644
index fb2d159..0000000
--- a/erpnext/templates/pages/cart.js
+++ /dev/null
@@ -1,303 +0,0 @@
-// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-// License: GNU General Public License v3. See license.txt
-
-// JS exclusive to /cart page
-frappe.provide("erpnext.e_commerce.shopping_cart");
-var shopping_cart = erpnext.e_commerce.shopping_cart;
-
-$.extend(shopping_cart, {
-	show_error: function(title, text) {
-		$("#cart-container").html('<div class="msg-box"><h4>' +
-			title + '</h4><p class="text-muted">' + text + '</p></div>');
-	},
-
-	bind_events: function() {
-		shopping_cart.bind_address_picker_dialog();
-		shopping_cart.bind_place_order();
-		shopping_cart.bind_request_quotation();
-		shopping_cart.bind_change_qty();
-		shopping_cart.bind_remove_cart_item();
-		shopping_cart.bind_change_notes();
-		shopping_cart.bind_coupon_code();
-	},
-
-	bind_address_picker_dialog: function() {
-		const d = this.get_update_address_dialog();
-		this.parent.find('.btn-change-address').on('click', (e) => {
-			const type = $(e.currentTarget).parents('.address-container').attr('data-address-type');
-			$(d.get_field('address_picker').wrapper).html(
-				this.get_address_template(type)
-			);
-			d.show();
-		});
-	},
-
-	get_update_address_dialog() {
-		let d = new frappe.ui.Dialog({
-			title: "Select Address",
-			fields: [{
-				'fieldtype': 'HTML',
-				'fieldname': 'address_picker',
-			}],
-			primary_action_label: __('Set Address'),
-			primary_action: () => {
-				const $card = d.$wrapper.find('.address-card.active');
-				const address_type = $card.closest('[data-address-type]').attr('data-address-type');
-				const address_name = $card.closest('[data-address-name]').attr('data-address-name');
-				frappe.call({
-					type: "POST",
-					method: "erpnext.e_commerce.shopping_cart.cart.update_cart_address",
-					freeze: true,
-					args: {
-						address_type,
-						address_name
-					},
-					callback: function(r) {
-						d.hide();
-						if (!r.exc) {
-							$(".cart-tax-items").html(r.message.total);
-							shopping_cart.parent.find(
-								`.address-container[data-address-type="${address_type}"]`
-							).html(r.message.address);
-						}
-					}
-				});
-			}
-		});
-
-		return d;
-	},
-
-	get_address_template(type) {
-		return {
-			shipping: `<div class="mb-3" data-section="shipping-address">
-				<div class="row no-gutters" data-fieldname="shipping_address_name">
-					{% for address in shipping_addresses %}
-						<div class="mr-3 mb-3 w-100" data-address-name="{{address.name}}" data-address-type="shipping"
-							{% if doc.shipping_address_name == address.name %} data-active {% endif %}>
-							{% include "templates/includes/cart/address_picker_card.html" %}
-						</div>
-					{% endfor %}
-				</div>
-			</div>`,
-			billing: `<div class="mb-3" data-section="billing-address">
-				<div class="row no-gutters" data-fieldname="customer_address">
-					{% for address in billing_addresses %}
-						<div class="mr-3 mb-3 w-100" data-address-name="{{address.name}}" data-address-type="billing"
-							{% if doc.shipping_address_name == address.name %} data-active {% endif %}>
-							{% include "templates/includes/cart/address_picker_card.html" %}
-						</div>
-					{% endfor %}
-				</div>
-			</div>`,
-		}[type];
-	},
-
-	bind_place_order: function() {
-		$(".btn-place-order").on("click", function() {
-			shopping_cart.place_order(this);
-		});
-	},
-
-	bind_request_quotation: function() {
-		$('.btn-request-for-quotation').on('click', function() {
-			shopping_cart.request_quotation(this);
-		});
-	},
-
-	bind_change_qty: function() {
-		// bind update button
-		$(".cart-items").on("change", ".cart-qty", function() {
-			var item_code = $(this).attr("data-item-code");
-			var newVal = $(this).val();
-			shopping_cart.shopping_cart_update({item_code, qty: newVal});
-		});
-
-		$(".cart-items").on('click', '.number-spinner button', function () {
-			var btn = $(this),
-				input = btn.closest('.number-spinner').find('input'),
-				oldValue = input.val().trim(),
-				newVal = 0;
-
-			if (btn.attr('data-dir') == 'up') {
-				newVal = parseInt(oldValue) + 1;
-			} else {
-				if (oldValue > 1) {
-					newVal = parseInt(oldValue) - 1;
-				}
-			}
-			input.val(newVal);
-
-			let notes = input.closest("td").siblings().find(".notes").text().trim();
-			var item_code = input.attr("data-item-code");
-			shopping_cart.shopping_cart_update({
-				item_code,
-				qty: newVal,
-				additional_notes: notes
-			});
-		});
-	},
-
-	bind_change_notes: function() {
-		$('.cart-items').on('change', 'textarea', function() {
-			const $textarea = $(this);
-			const item_code = $textarea.attr('data-item-code');
-			const qty = $textarea.closest('tr').find('.cart-qty').val();
-			const notes = $textarea.val();
-			shopping_cart.shopping_cart_update({
-				item_code,
-				qty,
-				additional_notes: notes
-			});
-		});
-	},
-
-	bind_remove_cart_item: function() {
-		$(".cart-items").on("click", ".remove-cart-item", (e) => {
-			const $remove_cart_item_btn = $(e.currentTarget);
-			var item_code = $remove_cart_item_btn.data("item-code");
-
-			shopping_cart.shopping_cart_update({
-				item_code: item_code,
-				qty: 0
-			});
-		});
-	},
-
-	render_tax_row: function($cart_taxes, doc, shipping_rules) {
-		var shipping_selector;
-		if(shipping_rules) {
-			shipping_selector = '<select class="form-control">' + $.map(shipping_rules, function(rule) {
-				return '<option value="' + rule[0] + '">' + rule[1] + '</option>' }).join("\n") +
-			'</select>';
-		}
-
-		var $tax_row = $(repl('<div class="row">\
-			<div class="col-md-9 col-sm-9">\
-				<div class="row">\
-					<div class="col-md-9 col-md-offset-3">' +
-					(shipping_selector || '<p>%(description)s</p>') +
-					'</div>\
-				</div>\
-			</div>\
-			<div class="col-md-3 col-sm-3 text-right">\
-				<p' + (shipping_selector ? ' style="margin-top: 5px;"' : "") + '>%(formatted_tax_amount)s</p>\
-			</div>\
-		</div>', doc)).appendTo($cart_taxes);
-
-		if(shipping_selector) {
-			$tax_row.find('select option').each(function(i, opt) {
-				if($(opt).html() == doc.description) {
-					$(opt).attr("selected", "selected");
-				}
-			});
-			$tax_row.find('select').on("change", function() {
-				shopping_cart.apply_shipping_rule($(this).val(), this);
-			});
-		}
-	},
-
-	apply_shipping_rule: function(rule, btn) {
-		return frappe.call({
-			btn: btn,
-			type: "POST",
-			method: "erpnext.e_commerce.shopping_cart.cart.apply_shipping_rule",
-			args: { shipping_rule: rule },
-			callback: function(r) {
-				if(!r.exc) {
-					shopping_cart.render(r.message);
-				}
-			}
-		});
-	},
-
-	place_order: function(btn) {
-		shopping_cart.freeze();
-
-		return frappe.call({
-			type: "POST",
-			method: "erpnext.e_commerce.shopping_cart.cart.place_order",
-			btn: btn,
-			callback: function(r) {
-				if(r.exc) {
-					shopping_cart.unfreeze();
-					var msg = "";
-					if(r._server_messages) {
-						msg = JSON.parse(r._server_messages || []).join("<br>");
-					}
-
-					$("#cart-error")
-						.empty()
-						.html(msg || frappe._("Something went wrong!"))
-						.toggle(true);
-				} else {
-					$(btn).hide();
-					window.location.href = '/orders/' + encodeURIComponent(r.message);
-				}
-			}
-		});
-	},
-
-	request_quotation: function(btn) {
-		shopping_cart.freeze();
-
-		return frappe.call({
-			type: "POST",
-			method: "erpnext.e_commerce.shopping_cart.cart.request_for_quotation",
-			btn: btn,
-			callback: function(r) {
-				if(r.exc) {
-					shopping_cart.unfreeze();
-					var msg = "";
-					if(r._server_messages) {
-						msg = JSON.parse(r._server_messages || []).join("<br>");
-					}
-
-					$("#cart-error")
-						.empty()
-						.html(msg || frappe._("Something went wrong!"))
-						.toggle(true);
-				} else {
-					$(btn).hide();
-					window.location.href = '/quotations/' + encodeURIComponent(r.message);
-				}
-			}
-		});
-	},
-
-	bind_coupon_code: function() {
-		$(".bt-coupon").on("click", function() {
-			shopping_cart.apply_coupon_code(this);
-		});
-	},
-
-	apply_coupon_code: function(btn) {
-		return frappe.call({
-			type: "POST",
-			method: "erpnext.e_commerce.shopping_cart.cart.apply_coupon_code",
-			btn: btn,
-			args : {
-				applied_code : $('.txtcoupon').val(),
-				applied_referral_sales_partner: $('.txtreferral_sales_partner').val()
-			},
-			callback: function(r) {
-				if (r && r.message){
-					location.reload();
-				}
-			}
-		});
-	}
-});
-
-frappe.ready(function() {
-	if (window.location.pathname === "/cart") {
-		$(".cart-icon").hide();
-	}
-	shopping_cart.parent = $(".cart-container");
-	shopping_cart.bind_events();
-});
-
-function show_terms() {
-	var html = $(".cart-terms").html();
-	frappe.msgprint(html);
-}
diff --git a/erpnext/templates/pages/cart.py b/erpnext/templates/pages/cart.py
deleted file mode 100644
index cadb46f..0000000
--- a/erpnext/templates/pages/cart.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-no_cache = 1
-
-from erpnext.e_commerce.shopping_cart.cart import get_cart_quotation
-
-
-def get_context(context):
-	context.body_class = "product-page"
-	context.update(get_cart_quotation())
diff --git a/erpnext/templates/pages/customer_reviews.html b/erpnext/templates/pages/customer_reviews.html
deleted file mode 100644
index 121bec3..0000000
--- a/erpnext/templates/pages/customer_reviews.html
+++ /dev/null
@@ -1,67 +0,0 @@
-{% extends "templates/web.html" %}
-{% from "erpnext/templates/includes/macros.html" import user_review, ratings_summary %}
-
-{% block title %} {{ _("Customer Reviews") }} {% endblock %}
-
-{% block page_content %}
-<div class="product-container reviews-full-page col-md-12">
-	{% if enable_reviews %}
-		<!-- Title and Action -->
-		<div class="w-100 mb-6 d-flex">
-			<div class="reviews-header col-9">
-				{{ _("Customer Reviews") }}
-			</div>
-
-			<div class="write-a-review-btn col-3">
-				<!-- Write a Review for legitimate users -->
-				{% if frappe.session.user != "Guest" and user_is_customer %}
-					<button class="btn btn-write-review"
-						data-web-item="{{ web_item }}">
-						{{ _("Write a Review") }}
-					</button>
-				{% endif %}
-			</div>
-		</div>
-
-		<!-- Summary -->
-		{{ ratings_summary(reviews, reviews_per_rating, average_rating, average_whole_rating, for_summary=True, total_reviews=total_reviews) }}
-
-
-		<!-- Reviews and Comments -->
-		<div class="mt-8">
-			{% if reviews %}
-				{{ user_review(reviews) }}
-
-				{% if not reviews | len >= total_reviews %}
-					<button class="btn btn-light btn-view-more mr-2 mt-4 mb-4 w-30"
-						data-web-item="{{ web_item }}">
-						{{ _("View More") }}
-					</button>
-				{% endif %}
-
-			{% else %}
-				<h6 class="text-muted mt-6">
-					{{ _("No Reviews") }}
-				</h6>
-			{% endif %}
-		</div>
-	{% else %}
-		<!-- If reviews are disabled -->
-		<div class="text-center">
-			<h3 class="text-muted mt-8">
-				{{ _("No Reviews") }}
-			</h3>
-		</div>
-	{% endif %}
-</div>
-
-{% endblock %}
-
-{% block base_scripts %}
-<!-- js should be loaded in body! -->
-<script type="text/javascript" src="/assets/frappe/js/lib/jquery/jquery.min.js"></script>
-<script type="text/javascript" src="/assets/js/frappe-web.min.js"></script>
-<script type="text/javascript" src="/assets/js/control.min.js"></script>
-<script type="text/javascript" src="/assets/js/dialog.min.js"></script>
-<script type="text/javascript" src="/assets/js/bootstrap-4-web.min.js"></script>
-{% endblock %}
\ No newline at end of file
diff --git a/erpnext/templates/pages/customer_reviews.py b/erpnext/templates/pages/customer_reviews.py
deleted file mode 100644
index c1f0c93..0000000
--- a/erpnext/templates/pages/customer_reviews.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-import frappe
-
-from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import (
-	get_shopping_cart_settings,
-)
-from erpnext.e_commerce.doctype.item_review.item_review import get_item_reviews
-from erpnext.e_commerce.doctype.website_item.website_item import check_if_user_is_customer
-
-
-def get_context(context):
-	context.body_class = "product-page"
-	context.no_cache = 1
-	context.full_page = True
-	context.reviews = None
-
-	if frappe.form_dict and frappe.form_dict.get("web_item"):
-		context.web_item = frappe.form_dict.get("web_item")
-		context.user_is_customer = check_if_user_is_customer()
-		context.enable_reviews = get_shopping_cart_settings().enable_reviews
-
-		if context.enable_reviews:
-			reviews_data = get_item_reviews(context.web_item)
-			context.update(reviews_data)
diff --git a/erpnext/templates/pages/home.html b/erpnext/templates/pages/home.html
index 27d966a..08e0432 100644
--- a/erpnext/templates/pages/home.html
+++ b/erpnext/templates/pages/home.html
@@ -17,9 +17,6 @@
 			<h3 class="d-block d-sm-none">{{ homepage.description }}</h3>
 		</div>
 
-		<div class="container">
-			<a href="{{ explore_link }}" class="mb-5 btn btn-primary">{{ _('Explore') }}</a>
-		</div>
 	</section>
 	{% elif homepage.hero_section_based_on == 'Slideshow' and slideshow %}
 	<section class="hero-section">
@@ -29,26 +26,6 @@
 		{{ render_homepage_section(homepage.hero_section_doc) }}
 	{% endif %}
 
-	{% if homepage.products %}
-	<section class="container section-products my-5">
-		<h3>{{ _('Products') }}</h3>
-
-		<div class="row">
-			{% for item in homepage.products %}
-			<div class="col-md-4 mb-4">
-				<div class="card h-100 justify-content-between">
-					<img class="card-img-top website-image-extra-large" src="{{ item.image }}" loading="lazy" alt="{{ item.item_name }}"></img>
-					<div class="card-body flex-grow-0">
-						<h5 class="card-title">{{ item.item_name }}</h5>
-						<a href="{{ item.route }}" class="card-link">{{ _('More details') }}</a>
-					</div>
-				</div>
-			</div>
-			{% endfor %}
-		</div>
-	</section>
-	{% endif %}
-
 	{% if blogs %}
 	<section class="container my-5">
 		<h3>{{ _('Publications') }}</h3>
diff --git a/erpnext/templates/pages/home.py b/erpnext/templates/pages/home.py
index 47fb89d..751a5b0 100644
--- a/erpnext/templates/pages/home.py
+++ b/erpnext/templates/pages/home.py
@@ -10,11 +10,6 @@
 def get_context(context):
 	homepage = frappe.get_cached_doc("Homepage")
 
-	for item in homepage.products:
-		route = frappe.db.get_value("Website Item", {"item_code": item.item_code}, "route")
-		if route:
-			item.route = "/" + route
-
 	homepage.title = homepage.title or homepage.company
 	context.title = homepage.title
 	context.homepage = homepage
@@ -52,5 +47,3 @@
 	context.metatags = context.metatags or frappe._dict({})
 	context.metatags.image = homepage.hero_image or None
 	context.metatags.description = homepage.description or None
-
-	context.explore_link = "/all-products"
diff --git a/erpnext/templates/pages/integrations/gocardless_checkout.html b/erpnext/templates/pages/integrations/gocardless_checkout.html
deleted file mode 100644
index 6072db4..0000000
--- a/erpnext/templates/pages/integrations/gocardless_checkout.html
+++ /dev/null
@@ -1,16 +0,0 @@
-{% extends "templates/web.html" %}
-
-{% block title %} Payment {% endblock %}
-
-{%- block header -%}{% endblock %}
-
-{% block script %}
-<script>{% include "templates/includes/integrations/gocardless_checkout.js" %}</script>
-{% endblock %}
-
-{%- block page_content -%}
-<p class='lead text-center'>
-	<span class='gocardless-loading'>{{ _("Loading Payment System") }}</span>
-</p>
-
-{% endblock %}
diff --git a/erpnext/templates/pages/integrations/gocardless_checkout.py b/erpnext/templates/pages/integrations/gocardless_checkout.py
deleted file mode 100644
index 655be52..0000000
--- a/erpnext/templates/pages/integrations/gocardless_checkout.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import json
-
-import frappe
-from frappe import _
-from frappe.utils import flt, get_url
-
-from erpnext.erpnext_integrations.doctype.gocardless_settings.gocardless_settings import (
-	get_gateway_controller,
-	gocardless_initialization,
-)
-
-no_cache = 1
-
-expected_keys = (
-	"amount",
-	"title",
-	"description",
-	"reference_doctype",
-	"reference_docname",
-	"payer_name",
-	"payer_email",
-	"order_id",
-	"currency",
-)
-
-
-def get_context(context):
-	context.no_cache = 1
-
-	# all these keys exist in form_dict
-	if not (set(expected_keys) - set(frappe.form_dict.keys())):
-		for key in expected_keys:
-			context[key] = frappe.form_dict[key]
-
-		context["amount"] = flt(context["amount"])
-
-		gateway_controller = get_gateway_controller(context.reference_docname)
-		context["header_img"] = frappe.db.get_value(
-			"GoCardless Settings", gateway_controller, "header_img"
-		)
-
-	else:
-		frappe.redirect_to_message(
-			_("Some information is missing"),
-			_("Looks like someone sent you to an incomplete URL. Please ask them to look into it."),
-		)
-		frappe.local.flags.redirect_location = frappe.local.response.location
-		raise frappe.Redirect
-
-
-@frappe.whitelist(allow_guest=True)
-def check_mandate(data, reference_doctype, reference_docname):
-	data = json.loads(data)
-
-	client = gocardless_initialization(reference_docname)
-
-	payer = frappe.get_doc("Customer", data["payer_name"])
-
-	if payer.customer_type == "Individual" and payer.customer_primary_contact is not None:
-		primary_contact = frappe.get_doc("Contact", payer.customer_primary_contact)
-		prefilled_customer = {
-			"company_name": payer.name,
-			"given_name": primary_contact.first_name,
-		}
-		if primary_contact.last_name is not None:
-			prefilled_customer.update({"family_name": primary_contact.last_name})
-
-		if primary_contact.email_id is not None:
-			prefilled_customer.update({"email": primary_contact.email_id})
-		else:
-			prefilled_customer.update({"email": frappe.session.user})
-
-	else:
-		prefilled_customer = {"company_name": payer.name, "email": frappe.session.user}
-
-	success_url = get_url(
-		"./integrations/gocardless_confirmation?reference_doctype="
-		+ reference_doctype
-		+ "&reference_docname="
-		+ reference_docname
-	)
-
-	try:
-		redirect_flow = client.redirect_flows.create(
-			params={
-				"description": _("Pay {0} {1}").format(data["amount"], data["currency"]),
-				"session_token": frappe.session.user,
-				"success_redirect_url": success_url,
-				"prefilled_customer": prefilled_customer,
-			}
-		)
-
-		return {"redirect_to": redirect_flow.redirect_url}
-
-	except Exception as e:
-		frappe.log_error("GoCardless Payment Error")
-		return {"redirect_to": "/integrations/payment-failed"}
diff --git a/erpnext/templates/pages/integrations/gocardless_confirmation.html b/erpnext/templates/pages/integrations/gocardless_confirmation.html
deleted file mode 100644
index d961c63..0000000
--- a/erpnext/templates/pages/integrations/gocardless_confirmation.html
+++ /dev/null
@@ -1,16 +0,0 @@
-{% extends "templates/web.html" %}
-
-{% block title %} Payment {% endblock %}
-
-{%- block header -%}{% endblock %}
-
-{% block script %}
-<script>{% include "templates/includes/integrations/gocardless_confirmation.js" %}</script>
-{% endblock %}
-
-{%- block page_content -%}
-<p class='lead text-center'>
-	<span class='gocardless-loading'>{{ _("Payment Confirmation") }}</span>
-</p>
-
-{% endblock %}
diff --git a/erpnext/templates/pages/integrations/gocardless_confirmation.py b/erpnext/templates/pages/integrations/gocardless_confirmation.py
deleted file mode 100644
index 559aa48..0000000
--- a/erpnext/templates/pages/integrations/gocardless_confirmation.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-from frappe import _
-
-from erpnext.erpnext_integrations.doctype.gocardless_settings.gocardless_settings import (
-	get_gateway_controller,
-	gocardless_initialization,
-)
-
-no_cache = 1
-
-expected_keys = ("redirect_flow_id", "reference_doctype", "reference_docname")
-
-
-def get_context(context):
-	context.no_cache = 1
-
-	# all these keys exist in form_dict
-	if not (set(expected_keys) - set(frappe.form_dict.keys())):
-		for key in expected_keys:
-			context[key] = frappe.form_dict[key]
-
-	else:
-		frappe.redirect_to_message(
-			_("Some information is missing"),
-			_("Looks like someone sent you to an incomplete URL. Please ask them to look into it."),
-		)
-		frappe.local.flags.redirect_location = frappe.local.response.location
-		raise frappe.Redirect
-
-
-@frappe.whitelist(allow_guest=True)
-def confirm_payment(redirect_flow_id, reference_doctype, reference_docname):
-
-	client = gocardless_initialization(reference_docname)
-
-	try:
-		redirect_flow = client.redirect_flows.complete(
-			redirect_flow_id, params={"session_token": frappe.session.user}
-		)
-
-		confirmation_url = redirect_flow.confirmation_url
-		gocardless_success_page = frappe.get_hooks("gocardless_success_page")
-		if gocardless_success_page:
-			confirmation_url = frappe.get_attr(gocardless_success_page[-1])(
-				reference_doctype, reference_docname
-			)
-
-		data = {
-			"mandate": redirect_flow.links.mandate,
-			"customer": redirect_flow.links.customer,
-			"redirect_to": confirmation_url,
-			"redirect_message": "Mandate successfully created",
-			"reference_doctype": reference_doctype,
-			"reference_docname": reference_docname,
-		}
-
-		try:
-			create_mandate(data)
-		except Exception as e:
-			frappe.log_error("GoCardless Mandate Registration Error")
-
-		gateway_controller = get_gateway_controller(reference_docname)
-		frappe.get_doc("GoCardless Settings", gateway_controller).create_payment_request(data)
-
-		return {"redirect_to": confirmation_url}
-
-	except Exception as e:
-		frappe.log_error("GoCardless Payment Error")
-		return {"redirect_to": "/integrations/payment-failed"}
-
-
-def create_mandate(data):
-	data = frappe._dict(data)
-	frappe.logger().debug(data)
-
-	mandate = data.get("mandate")
-
-	if frappe.db.exists("GoCardless Mandate", mandate):
-		return
-
-	else:
-		reference_doc = frappe.db.get_value(
-			data.get("reference_doctype"),
-			data.get("reference_docname"),
-			["reference_doctype", "reference_name"],
-			as_dict=1,
-		)
-		erpnext_customer = frappe.db.get_value(
-			reference_doc.reference_doctype, reference_doc.reference_name, ["customer_name"], as_dict=1
-		)
-
-		try:
-			frappe.get_doc(
-				{
-					"doctype": "GoCardless Mandate",
-					"mandate": mandate,
-					"customer": erpnext_customer.customer_name,
-					"gocardless_customer": data.get("customer"),
-				}
-			).insert(ignore_permissions=True)
-
-		except Exception:
-			frappe.log_error("Gocardless: Unable to create mandate")
diff --git a/erpnext/templates/pages/order.html b/erpnext/templates/pages/order.html
index bc34ad5..97bf487 100644
--- a/erpnext/templates/pages/order.html
+++ b/erpnext/templates/pages/order.html
@@ -1,5 +1,5 @@
 {% extends "templates/web.html" %}
-{% from "erpnext/templates/includes/order/order_macros.html" import item_name_and_description %}
+{% from "erpnext/templates/includes/macros.html" import product_image %}
 
 {% block breadcrumbs %}
 	{% include "templates/includes/breadcrumbs.html" %}
@@ -34,18 +34,6 @@
 				</a>
 			</ul>
 		</div>
-		{% if show_pay_button %}
-			<div class="form-column col-sm-6">
-				<div class="page-header-actions-block" data-html-block="header-actions">
-					<p>
-						<a href="/api/method/erpnext.accounts.doctype.payment_request.payment_request.make_payment_request?dn={{ doc.name }}&dt={{ doc.doctype }}&submit_doc=1&order_type=Shopping Cart"
-							class="btn btn-primary btn-sm" id="pay-for-order">
-							{{ _("Pay") }} {{doc.get_formatted("grand_total") }}
-						</a>
-					</p>
-				</div>
-			</div>
-		{% endif %}
 	</div>
 {% endblock %}
 
@@ -130,42 +118,6 @@
 		</div>
 	</div>
 
-	{% if enabled_checkout and ((doc.doctype=="Sales Order" and doc.per_billed <= 0)
-		or (doc.doctype=="Sales Invoice" and doc.outstanding_amount> 0)) %}
-		<div class="panel panel-default">
-			<div class="panel-collapse">
-				<div class="panel-body text-muted small">
-					<div class="row">
-						<div class="form-column col-sm-6">
-							{% if available_loyalty_points %}
-							<div class="panel-heading">
-								<div class="row">
-									<div class="form-column col-sm-6 address-title">
-										<strong>Loyalty Points</strong>
-									</div>
-								</div>
-							</div>
-
-							<div class="form-group">
-								<div class="h6">Enter Loyalty Points</div>
-								<div class="control-input-wrapper">
-									<div class="control-input">
-										<input class="form-control" type="number" min="0"
-											max="{{ available_loyalty_points }}" id="loyalty-point-to-redeem">
-									</div>
-									<p class="help-box small text-muted d-none d-sm-block"> Available Points: {{
-										available_loyalty_points }} </p>
-								</div>
-							</div>
-							{% endif %}
-						</div>
-					</div>
-				</div>
-			</div>
-		</div>
-	{% endif %}
-
-
 	{% if attachments %}
 		<div class="order-item-table">
 			<div class="row order-items order-item-header text-muted">
@@ -193,15 +145,27 @@
 	{% endif %}
 {% endblock %}
 
-{% block script %}
-	<script> {% include "templates/pages/order.js" %}</script>
-	<script>
-		window.doc_info = {
-			customer: '{{doc.customer}}',
-			doctype: '{{ doc.doctype }}',
-			doctype_name: '{{ doc.name }}',
-			grand_total: '{{ doc.grand_total }}',
-			currency: '{{ doc.currency }}'
-		}
-	</script>
-{% endblock %}
\ No newline at end of file
+{% macro item_name_and_description(d) %}
+	<div class="row item_name_and_description">
+		<div class="col-xs-4 col-sm-2 order-image-col">
+			<div class="order-image h-100">
+				{% if d.thumbnail or d.image %}
+					{{ product_image(d.thumbnail or d.image, no_border=True) }}
+				{% else %}
+					<div class="no-image-cart-item" style="min-height: 100px;">
+						{{ frappe.utils.get_abbr(d.item_name) or "NA" }}
+					</div>
+				{% endif %}
+			</div>
+		</div>
+		<div class="col-xs-8 col-sm-10">
+			{{ d.item_code }}
+			<div class="text-muted small item-description">
+				{{ html2text(d.description) | truncate(140) }}
+			</div>
+			<span class="text-muted mt-2 d-l-n order-qty">
+				{{ _("Qty ") }}({{ d.get_formatted("qty") }})
+			</span>
+		</div>
+	</div>
+{% endmacro %}
\ No newline at end of file
diff --git a/erpnext/templates/pages/order.js b/erpnext/templates/pages/order.js
deleted file mode 100644
index 0574cde..0000000
--- a/erpnext/templates/pages/order.js
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ready(function(){
-
-	var loyalty_points_input = document.getElementById("loyalty-point-to-redeem");
-	var loyalty_points_status = document.getElementById("loyalty-points-status");
-	if (loyalty_points_input) {
-		loyalty_points_input.onblur = apply_loyalty_points;
-	}
-
-	function apply_loyalty_points() {
-		var loyalty_points = parseInt(loyalty_points_input.value);
-		if (loyalty_points) {
-			frappe.call({
-				method: "erpnext.accounts.doctype.loyalty_program.loyalty_program.get_redeemption_factor",
-				args: {
-					"customer": doc_info.customer
-				},
-				callback: function(r) {
-					if (r) {
-						var message = ""
-						let loyalty_amount = flt(r.message*loyalty_points);
-						if (doc_info.grand_total && doc_info.grand_total < loyalty_amount) {
-							let redeemable_amount = parseInt(doc_info.grand_total/r.message);
-							message = "You can only redeem max " + redeemable_amount + " points in this order.";
-							frappe.msgprint(__(message));
-						} else {
-							message = loyalty_points + " Loyalty Points of amount "+ loyalty_amount + " is applied."
-							frappe.msgprint(__(message));
-							var remaining_amount = flt(doc_info.grand_total) - flt(loyalty_amount);
-							var payment_button = document.getElementById("pay-for-order");
-							payment_button.innerHTML = __("Pay Remaining");
-							payment_button.href = "/api/method/erpnext.accounts.doctype.payment_request.payment_request.make_payment_request?dn="+doc_info.doctype_name+"&dt="+doc_info.doctype+"&loyalty_points="+loyalty_points+"&submit_doc=1&order_type=Shopping Cart";
-						}
-						loyalty_points_status.innerHTML = message;
-					}
-				}
-			});
-		}
-	}
-})
diff --git a/erpnext/templates/pages/order.py b/erpnext/templates/pages/order.py
index 13772d3..d0968bf 100644
--- a/erpnext/templates/pages/order.py
+++ b/erpnext/templates/pages/order.py
@@ -4,8 +4,6 @@
 import frappe
 from frappe import _
 
-from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import show_attachments
-
 
 def get_context(context):
 	context.no_cache = 1
@@ -14,17 +12,12 @@
 	if hasattr(context.doc, "set_indicator"):
 		context.doc.set_indicator()
 
-	if show_attachments():
-		context.attachments = get_attachments(frappe.form_dict.doctype, frappe.form_dict.name)
-
 	context.parents = frappe.form_dict.parents
 	context.title = frappe.form_dict.name
 	context.payment_ref = frappe.db.get_value(
 		"Payment Request", {"reference_name": frappe.form_dict.name}, "name"
 	)
 
-	context.enabled_checkout = frappe.get_doc("E Commerce Settings").enable_checkout
-
 	default_print_format = frappe.db.get_value(
 		"Property Setter",
 		dict(property="default_print_format", doc_type=frappe.form_dict.doctype),
diff --git a/erpnext/templates/pages/product_search.html b/erpnext/templates/pages/product_search.html
deleted file mode 100644
index 6a5425b..0000000
--- a/erpnext/templates/pages/product_search.html
+++ /dev/null
@@ -1,32 +0,0 @@
-{% extends "templates/web.html" %}
-
-{% block title %} {{ _("Product Search") }} {% endblock %}
-
-{% block header %}<h2>{{ _("Product Search") }}</h2>{% endblock %}
-
-{% block page_content %}
-<script>{% include "templates/includes/product_list.js" %}</script>
-
-<script>
-frappe.ready(function() {
-	var txt = frappe.utils.get_url_arg("search");
-	$(".search-results").html('{{ _("Search results for") + ": " + html2text(frappe.form_dict.search or "") | e | trim }}');
-	window.search = txt;
-	window.start = 0;
-	window.get_product_list();
-});
-</script>
-
-<div class="product-search-content">
-    <h3 class="search-results">{{ _("Search Results") }}</h3>
-	<div id="search-list" class="row">
-
-	</div>
-	<div style="text-align: center;">
-		<div class="more-btn"
-			style="display: none; text-align: center;">
-            <button class="btn btn-light">{{ _("More...") }}</button>
-		</div>
-	</div>
-</div>
-{% endblock %}
diff --git a/erpnext/templates/pages/product_search.py b/erpnext/templates/pages/product_search.py
deleted file mode 100644
index f40fd47..0000000
--- a/erpnext/templates/pages/product_search.py
+++ /dev/null
@@ -1,152 +0,0 @@
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import json
-
-import frappe
-from frappe.utils import cint, cstr
-from redis.commands.search.query import Query
-
-from erpnext.e_commerce.redisearch_utils import (
-	WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE,
-	WEBSITE_ITEM_INDEX,
-	WEBSITE_ITEM_NAME_AUTOCOMPLETE,
-	is_redisearch_enabled,
-)
-from erpnext.e_commerce.shopping_cart.product_info import set_product_info_for_website
-from erpnext.setup.doctype.item_group.item_group import get_item_for_list_in_html
-
-no_cache = 1
-
-
-def get_context(context):
-	context.show_search = True
-
-
-@frappe.whitelist(allow_guest=True)
-def get_product_list(search=None, start=0, limit=12):
-	data = get_product_data(search, start, limit)
-
-	for item in data:
-		set_product_info_for_website(item)
-
-	return [get_item_for_list_in_html(r) for r in data]
-
-
-def get_product_data(search=None, start=0, limit=12):
-	# limit = 12 because we show 12 items in the grid view
-	# base query
-	query = """
-		SELECT
-			web_item_name, item_name, item_code, brand, route,
-			website_image, thumbnail, item_group,
-			description, web_long_description as website_description,
-			website_warehouse, ranking
-		FROM `tabWebsite Item`
-		WHERE published = 1
-		"""
-
-	# search term condition
-	if search:
-		query += """ and (item_name like %(search)s
-				or web_item_name like %(search)s
-				or brand like %(search)s
-				or web_long_description like %(search)s)"""
-		search = "%" + cstr(search) + "%"
-
-	# order by
-	query += """ ORDER BY ranking desc, modified desc limit %s offset %s""" % (
-		cint(limit),
-		cint(start),
-	)
-
-	return frappe.db.sql(query, {"search": search}, as_dict=1)  # nosemgrep
-
-
-@frappe.whitelist(allow_guest=True)
-def search(query):
-	product_results = product_search(query)
-	category_results = get_category_suggestions(query)
-
-	return {
-		"product_results": product_results.get("results") or [],
-		"category_results": category_results.get("results") or [],
-	}
-
-
-@frappe.whitelist(allow_guest=True)
-def product_search(query, limit=10, fuzzy_search=True):
-	search_results = {"from_redisearch": True, "results": []}
-
-	if not is_redisearch_enabled():
-		# Redisearch module not enabled
-		search_results["from_redisearch"] = False
-		search_results["results"] = get_product_data(query, 0, limit)
-		return search_results
-
-	if not query:
-		return search_results
-
-	redis = frappe.cache()
-	query = clean_up_query(query)
-
-	# TODO: Check perf/correctness with Suggestions & Query vs only Query
-	# TODO: Use Levenshtein Distance in Query (max=3)
-	redisearch = redis.ft(WEBSITE_ITEM_INDEX)
-	suggestions = redisearch.sugget(
-		WEBSITE_ITEM_NAME_AUTOCOMPLETE,
-		query,
-		num=limit,
-		fuzzy=fuzzy_search and len(query) > 3,
-	)
-
-	# Build a query
-	query_string = query
-
-	for s in suggestions:
-		query_string += f"|('{clean_up_query(s.string)}')"
-
-	q = Query(query_string)
-	results = redisearch.search(q)
-
-	search_results["results"] = list(map(convert_to_dict, results.docs))
-	search_results["results"] = sorted(
-		search_results["results"], key=lambda k: frappe.utils.cint(k["ranking"]), reverse=True
-	)
-
-	return search_results
-
-
-def clean_up_query(query):
-	return "".join(c for c in query if c.isalnum() or c.isspace())
-
-
-def convert_to_dict(redis_search_doc):
-	return redis_search_doc.__dict__
-
-
-@frappe.whitelist(allow_guest=True)
-def get_category_suggestions(query):
-	search_results = {"results": []}
-
-	if not is_redisearch_enabled():
-		# Redisearch module not enabled, query db
-		categories = frappe.db.get_all(
-			"Item Group",
-			filters={"name": ["like", "%{0}%".format(query)], "show_in_website": 1},
-			fields=["name", "route"],
-		)
-		search_results["results"] = categories
-		return search_results
-
-	if not query:
-		return search_results
-
-	ac = frappe.cache().ft()
-	suggestions = ac.sugget(WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE, query, num=10, with_payloads=True)
-
-	results = [json.loads(s.payload) for s in suggestions]
-
-	search_results["results"] = results
-
-	return search_results
diff --git a/erpnext/templates/pages/rfq.html b/erpnext/templates/pages/rfq.html
index 6516482..d371bf2 100644
--- a/erpnext/templates/pages/rfq.html
+++ b/erpnext/templates/pages/rfq.html
@@ -1,7 +1,7 @@
 {% extends "templates/web.html" %}
 
 {% block header %}
-<h1>{{ doc.name }}</h1>
+<h1 style="margin-top: 10px;">{{ doc.name }}</h1>
 {% endblock %}
 
 {% block script %}
@@ -16,7 +16,7 @@
 {% if doc.items %}
 <button class="btn btn-primary btn-sm"
     type="button">
-    {{ _("Submit") }}</button>
+    {{ _("Make Quotation") }}</button>
 {% endif %}
 {% endblock %}
 
diff --git a/erpnext/templates/pages/wishlist.html b/erpnext/templates/pages/wishlist.html
deleted file mode 100644
index 7a81ded..0000000
--- a/erpnext/templates/pages/wishlist.html
+++ /dev/null
@@ -1,28 +0,0 @@
-{% extends "templates/web.html" %}
-
-{% block title %} {{ _("Wishlist") }} {% endblock %}
-
-{% block header %}<h3 class="shopping-cart-header mt-2 mb-6">{{ _("Wishlist") }}</h1>{% endblock %}
-
-{% block page_content %}
-{% if items %}
-	<div class="row">
-		<div class="col-md-12 item-card-group-section">
-			<div class="row products-list">
-					{% from "erpnext/templates/includes/macros.html" import wishlist_card %}
-					{% for item in items %}
-						{{ wishlist_card(item, settings) }}
-					{% endfor %}
-			</div>
-		</div>
-	</div>
-{% else %}
-	<div class="cart-empty frappe-card">
-		<div class="cart-empty-state">
-			<img src="/assets/erpnext/images/ui-states/cart-empty-state.png" alt="Empty Cart">
-		</div>
-		<div class="cart-empty-message mt-4">{{ _('Wishlist is empty!') }}</p>
-	</div>
-{% endif %}
-
-{% endblock %}
\ No newline at end of file
diff --git a/erpnext/templates/pages/wishlist.py b/erpnext/templates/pages/wishlist.py
deleted file mode 100644
index 17607e4..0000000
--- a/erpnext/templates/pages/wishlist.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-import frappe
-
-from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import (
-	get_shopping_cart_settings,
-)
-from erpnext.e_commerce.shopping_cart.cart import _set_price_list
-from erpnext.utilities.product import get_price
-
-
-def get_context(context):
-	is_guest = frappe.session.user == "Guest"
-
-	settings = get_shopping_cart_settings()
-	items = get_wishlist_items() if not is_guest else []
-	selling_price_list = _set_price_list(settings) if not is_guest else None
-
-	items = set_stock_price_details(items, settings, selling_price_list)
-
-	context.body_class = "product-page"
-	context.items = items
-	context.settings = settings
-	context.no_cache = 1
-
-
-def get_stock_availability(item_code, warehouse):
-	from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
-
-	if warehouse and frappe.get_cached_value("Warehouse", warehouse, "is_group") == 1:
-		warehouses = get_child_warehouses(warehouse)
-	else:
-		warehouses = [warehouse] if warehouse else []
-
-	stock_qty = 0.0
-	for warehouse in warehouses:
-		stock_qty += frappe.utils.flt(
-			frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "actual_qty")
-		)
-
-	return bool(stock_qty)
-
-
-def get_wishlist_items():
-	if not frappe.db.exists("Wishlist", frappe.session.user):
-		return []
-
-	return frappe.db.get_all(
-		"Wishlist Item",
-		filters={"parent": frappe.session.user},
-		fields=[
-			"web_item_name",
-			"item_code",
-			"item_name",
-			"website_item",
-			"warehouse",
-			"image",
-			"item_group",
-			"route",
-		],
-	)
-
-
-def set_stock_price_details(items, settings, selling_price_list):
-	for item in items:
-		if settings.show_stock_availability:
-			item.available = get_stock_availability(item.item_code, item.get("warehouse"))
-
-		price_details = get_price(
-			item.item_code, selling_price_list, settings.default_customer_group, settings.company
-		)
-
-		if price_details:
-			item.formatted_price = price_details.get("formatted_price")
-			item.formatted_mrp = price_details.get("formatted_mrp")
-			if item.formatted_mrp:
-				item.discount = price_details.get("formatted_discount_percent") or price_details.get(
-					"formatted_discount_rate"
-				)
-
-	return items
diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv
index 79777f2..79b9574 100644
--- a/erpnext/translations/de.csv
+++ b/erpnext/translations/de.csv
@@ -4586,7 +4586,7 @@
 Tax Withholding Category,Steuereinbehalt Kategorie,
 Edit Posting Date and Time,Buchungsdatum und -uhrzeit bearbeiten,
 Is Paid,Ist bezahlt,
-Is Return (Debit Note),ist Rücklieferung (Lastschrift),
+Is Return (Debit Note),Ist Rechnungskorrektur (Retoure),
 Apply Tax Withholding Amount,Steuereinbehaltungsbetrag anwenden,
 Accounting Dimensions ,Buchhaltung Dimensionen,
 Supplier Invoice Details,Lieferant Rechnungsdetails,
@@ -4710,7 +4710,7 @@
 ACC-SINV-.YYYY.-,ACC-SINV-.JJJJ.-,
 Include Payment (POS),(POS) Zahlung einschließen,
 Offline POS Name,Offline-Verkaufsstellen-Name,
-Is Return (Credit Note),ist Rücklieferung (Gutschrift),
+Is Return (Credit Note),Ist Rechnungskorrektur (Retoure),
 Update Billed Amount in Sales Order,Aktualisierung des Rechnungsbetrags im Auftrag,
 Customer PO Details,Auftragsdetails,
 Customer's Purchase Order,Bestellung des Kunden,
@@ -6998,7 +6998,7 @@
 Tariff Number,Tarifnummer,
 Delivery To,Lieferung an,
 MAT-DN-.YYYY.-,MAT-DN-.YYYY.-,
-Is Return,Ist Rückgabe,
+Is Return,Ist Retoure,
 Issue Credit Note,Gutschrift ausgeben,
 Return Against Delivery Note,Zurück zum Lieferschein,
 Customer's Purchase Order No,Bestellnummer des Kunden,
diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv
index 32fe9ff..d3875c1 100644
--- a/erpnext/translations/fr.csv
+++ b/erpnext/translations/fr.csv
@@ -21,9 +21,6 @@
 A customer with the same name already exists,Un client avec un nom identique existe déjà,
 A question must have more than one options,Une question doit avoir plus d'une option,
 A qustion must have at least one correct options,Une qustion doit avoir au moins une des options correctes,
-A4,A4,
-API Endpoint,API Endpoint,
-API Key,Clé API,
 Abbr can not be blank or space,Abré. ne peut être vide ou contenir un espace,
 Abbreviation already used for another company,Abréviation déjà utilisée pour une autre société,
 Abbreviation cannot have more than 5 characters,L'abbréviation ne peut pas avoir plus de 5 caractères,
@@ -36,9 +33,7 @@
 Academic Year,Année académique,
 Academic Year: ,Année scolaire:,
 Accepted + Rejected Qty must be equal to Received quantity for Item {0},La Qté Acceptée + Rejetée doit être égale à la quantité Reçue pour l'Article {0},
-Access Token,Jeton d'Accès,
 Accessable Value,Valeur accessible,
-Account,Compte,
 Account Number,Numéro de compte,
 Account Number {0} already used in account {1},Numéro de compte {0} déjà utilisé dans le compte {1},
 Account Pay Only,Compte Bénéficiaire Seulement,
@@ -75,12 +70,10 @@
 Accounting Ledger,Livre des Comptes,
 Accounting journal entries.,Les écritures comptables.,
 Accounts,Comptes,
-Accounts Manager,Responsable des Comptes,
 Accounts Payable,Comptes Créditeurs,
 Accounts Payable Summary,Résumé des Comptes Créditeurs,
 Accounts Receivable,Comptes débiteurs,
 Accounts Receivable Summary,Résumé des Comptes Débiteurs,
-Accounts User,Comptable,
 Accounts table cannot be blank.,Le tableau de comptes ne peut être vide.,
 Accumulated Depreciation,Amortissement Cumulé,
 Accumulated Depreciation Amount,Montant d'Amortissement Cumulé,
@@ -89,10 +82,8 @@
 Accumulated Values,Valeurs accumulées,
 Accumulated Values in Group Company,Valeurs accumulées dans la société mère,
 Achieved ({}),Atteint ({}),
-Action,Action,
 Action Initialised,Action initialisée,
 Actions,Actions,
-Active,actif,
 Activity Cost exists for Employee {0} against Activity Type - {1},Des Coûts d'Activité existent pour l'Employé {0} pour le Type d'Activité - {1},
 Activity Cost per Employee,Coût de l'Activité par Employé,
 Activity Type,Type d'activité,
@@ -104,7 +95,6 @@
 Actual Qty: Quantity available in the warehouse.,Quantité réelle : Quantité disponible dans l'entrepôt .,
 Actual qty in stock,Qté réelle en stock,
 Actual type tax cannot be included in Item rate in row {0},Le type de taxe réel ne peut pas être inclus dans le prix de l'Article à la ligne {0},
-Add,Ajouter,
 Add / Edit Prices,Ajouter / Modifier Prix,
 Add Comment,Ajouter un Commentaire,
 Add Customers,Ajouter des clients,
@@ -127,17 +117,11 @@
 Add notes,Ajouter des notes,
 Add the rest of your organization as your users. You can also add invite Customers to your portal by adding them from Contacts,Ajouter le reste de votre organisation en tant qu'utilisateurs. Vous pouvez aussi inviter des Clients sur votre portail en les ajoutant depuis les Contacts,
 Add/Remove Recipients,Ajouter/Supprimer des Destinataires,
-Added,Ajouté,
 Added {0} users,{0} utilisateurs ajoutés,
 Additional Salary Component Exists.,La composante salariale supplémentaire existe.,
-Address,Adresse,
-Address Line 2,Adresse Ligne 2,
 Address Name,Nom de l'Adresse,
-Address Title,Titre de l'Adresse,
-Address Type,Type d'Adresse,
 Administrative Expenses,Charges Administratives,
 Administrative Officer,Agent administratif,
-Administrator,Administrateur,
 Admission,Admission,
 Admission and Enrollment,Admission et inscription,
 Admissions for {0},Admissions pour {0},
@@ -171,7 +155,6 @@
 All BOMs,Toutes les nomenclatures,
 All Contacts.,Tous les contacts.,
 All Customer Groups,Tous les Groupes Client,
-All Day,Toute la Journée,
 All Departments,Tous les départements,
 All Healthcare Service Units,Tous les services de soins de santé,
 All Item Groups,Tous les Groupes d'Articles,
@@ -193,8 +176,6 @@
 "Already set default in pos profile {0} for user {1}, kindly disabled default","Déjà défini par défaut dans le profil pdv {0} pour l'utilisateur {1}, veuillez désactiver la valeur par défaut",
 Alternate Item,Article alternatif,
 Alternative item must not be same as item code,L'article alternatif ne doit pas être le même que le code article,
-Amended From,Modifié Depuis,
-Amount,Montant,
 Amount After Depreciation,Montant après amortissement,
 Amount of Integrated Tax,Montant de la taxe intégrée,
 Amount of TDS Deducted,Quantité de TDS déduite,
@@ -216,7 +197,6 @@
 Another Sales Person {0} exists with the same Employee id,Un autre Commercial {0} existe avec le même ID d'Employé,
 Antibiotic,Antibiotique,
 Apparel & Accessories,Vêtements & Accessoires,
-Applicable For,Applicable Pour,
 "Applicable if the company is SpA, SApA or SRL","Applicable si la société est SpA, SApA ou SRL",
 Applicable if the company is a limited liability company,Applicable si la société est une société à responsabilité limitée,
 Applicable if the company is an Individual or a Proprietorship,Applicable si la société est un particulier ou une entreprise,
@@ -264,15 +244,12 @@
 Asset {0} does not belong to company {1},L'actif {0} ne fait pas partie à la société {1},
 Asset {0} must be submitted,L'actif {0} doit être soumis,
 Assets,Actifs - Immo.,
-Assign To,Attribuer À,
 Associate,Associé,
 At least one mode of payment is required for POS invoice.,Au moins un mode de paiement est nécessaire pour une facture de PDV,
 Atleast one item should be entered with negative quantity in return document,Au moins un article doit être saisi avec quantité négative dans le document de retour,
 Atleast one of the Selling or Buying must be selected,Au moins Vente ou Achat doit être sélectionné,
 Atleast one warehouse is mandatory,Au moins un entrepôt est obligatoire,
 Attach Logo,Attacher le logo,
-Attachment,Pièce jointe,
-Attachments,Pièces jointes,
 Attendance can not be marked for future dates,La présence ne peut pas être marquée pour les dates à venir,
 Attendance date can not be less than employee's joining date,Date de présence ne peut pas être antérieure à la date d'embauche de l'employé,
 Attendance for employee {0} is already marked,La présence de l'employé {0} est déjà marquée,
@@ -282,7 +259,6 @@
 Attribute {0} selected multiple times in Attributes Table,Attribut {0} sélectionné à plusieurs reprises dans le Tableau des Attributs,
 Authorized Signatory,Signataire Autorisé,
 Auto Material Requests Generated,Demandes de Matériel Générées Automatiquement,
-Auto Repeat,Répétition automatique,
 Auto repeat document updated,Document de répétition automatique mis à jour,
 Automotive,Automobile,
 Available,Disponible,
@@ -332,8 +308,6 @@
 Banking and Payments,Banque et paiements,
 Barcode {0} already used in Item {1},Le Code Barre {0} est déjà utilisé dans l'article {1},
 Barcode {0} is not a valid {1} code,Le code-barres {0} n'est pas un code {1} valide,
-Base URL,URL de base,
-Based On,Basé Sur,
 Based On Payment Terms,Basé sur les conditions de paiement,
 Batch,Lot,
 Batch Entries,Entrées de lot,
@@ -406,7 +380,6 @@
 Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total',Peut se référer à ligne seulement si le type de charge est 'Montant de la ligne précedente' ou 'Total des lignes précedente',
 "Can't change valuation method, as there are transactions against some items which does not have it's own valuation method","Impossible de modifier la méthode de valorisation, car il existe des transactions sur certains articles ne possèdant pas leur propre méthode de valorisation",
 Can't create standard criteria. Please rename the criteria,Impossible de créer des critères standard. Veuillez renommer les critères,
-Cancel,Annuler,
 Cancel Material Visit {0} before cancelling this Warranty Claim,Annuler la Visite Matérielle {0} avant d'annuler cette Réclamation de Garantie,
 Cancel Material Visits {0} before cancelling this Maintenance Visit,Annuler les Visites Matérielles {0} avant d'annuler cette Visite de Maintenance,
 Cancel Subscription,Annuler l'abonnement,
@@ -459,8 +432,6 @@
 Cash In Hand,Liquidités,
 Cash or Bank Account is mandatory for making payment entry,Espèces ou Compte Bancaire est obligatoire pour réaliser une écriture de paiement,
 Cashier Closing,Fermeture de la caisse,
-Category,Catégorie,
-Category Name,Nom de la Catégorie,
 Caution,Mise en garde,
 Central Tax,Taxe centrale,
 Certification,Certification,
@@ -488,32 +459,22 @@
 Child nodes can be only created under 'Group' type nodes,Les noeuds enfants peuvent être créés uniquement dans les nœuds de type 'Groupe',
 Child warehouse exists for this warehouse. You can not delete this warehouse.,Un entrepôt enfant existe pour cet entrepôt. Vous ne pouvez pas supprimer cet entrepôt.,
 Circular Reference Error,Erreur de référence circulaire,
-City,Ville,
-City/Town,Ville,
 Clay,Argile,
-Clear filters,Effacer les filtres,
 Clear values,Des valeurs claires,
 Clearance Date,Date de Compensation,
 Clearance Date not mentioned,Date de Compensation non indiquée,
 Clearance Date updated,Date de Compensation mise à jour,
-Client,Client,
-Client ID,ID Client,
-Client Secret,Secret Client,
 Clinical Procedure,Procédure clinique,
 Clinical Procedure Template,Modèle de procédure clinique,
 Close Balance Sheet and book Profit or Loss.,Clôturer Bilan et Compte de Résultats.,
 Close Loan,Prêt proche,
 Close the POS,Clôturer le point de vente,
-Closed,Fermé,
 Closed order cannot be cancelled. Unclose to cancel.,Les commandes fermées ne peuvent être annulées. Réouvrir pour annuler.,
 Closing (Cr),Fermeture (Cr),
 Closing (Dr),Fermeture (Dr),
 Closing (Opening + Total),Fermeture (ouverture + total),
 Closing Account {0} must be of type Liability / Equity,Le Compte Clôturé {0} doit être de type Passif / Capitaux Propres,
 Closing Balance,Solde de clôture,
-Code,Code,
-Collapse All,Tout réduire,
-Color,Couleur,
 Colour,Couleur,
 Combined invoice portion must equal 100%,La portion combinée de la facture doit être égale à 100%,
 Commercial,Commercial,
@@ -525,7 +486,6 @@
 Company (not Customer or Supplier) master.,Données de base de la Société (ni les Clients ni les Fournisseurs),
 Company Abbreviation,Abréviation de la Société,
 Company Abbreviation cannot have more than 5 characters,L'abréviation de l'entreprise ne peut pas comporter plus de 5 caractères,
-Company Name,Nom de la Société,
 Company Name cannot be Company,Nom de la Société ne peut pas être Company,
 Company currencies of both the companies should match for Inter Company Transactions.,Les devises des deux sociétés doivent correspondre pour les transactions inter-sociétés.,
 Company is manadatory for company account,La société est le maître d'œuvre du compte d'entreprise,
@@ -535,7 +495,6 @@
 Complaint,Plainte,
 Completion Date,Date d'Achèvement,
 Computer,Ordinateur,
-Condition,Conditions,
 Configure,Configurer,
 Configure {0},Configurer {0},
 Confirmed orders from Customers.,Commandes confirmées des clients.,
@@ -552,11 +511,8 @@
 Consumed Amount,Montant Consommé,
 Consumed Qty,Qté Consommée,
 Consumer Products,Produits de Consommation,
-Contact,Contact,
 Contact Us,Contactez nous,
-Content,Contenu,
 Content Masters,Masters de contenu,
-Content Type,Type de Contenu,
 Continue Configuration,Continuer la configuration,
 Contract,Contrat,
 Contract End Date must be greater than Date of Joining,La Date de Fin de Contrat doit être supérieure à la Date d'Embauche,
@@ -597,7 +553,6 @@
 Course Schedule,Horaire du cours,
 Course: ,Cours:,
 Cr,Cr,
-Create,Créer,
 Create BOM,Créer une nomenclature,
 Create Delivery Trip,Créer un voyage de livraison,
 Create Employee,Créer un employé,
@@ -673,8 +628,6 @@
 Current Liabilities,Dettes Actuelles,
 Current Qty,Qté actuelle,
 Current invoice {0} is missing,La facture en cours {0} est manquante,
-Custom HTML,HTML Personnalisé,
-Custom?,Personnaliser ?,
 Customer,Client,
 Customer Addresses And Contacts,Adresses et Contacts des Clients,
 Customer Contact,Contact client,
@@ -699,7 +652,6 @@
 Data Import and Export,Importer et Exporter des Données,
 Data Import and Settings,Importation de données et paramètres,
 Database of potential customers.,Base de données de clients potentiels.,
-Date Format,Format de Date,
 Date Of Retirement must be greater than Date of Joining,La Date de Départ à la Retraite doit être supérieure à Date d'Embauche,
 Date of Birth,Date de naissance,
 Date of Birth cannot be greater than today.,Date de Naissance ne peut être après la Date du Jour.,
@@ -707,7 +659,6 @@
 Date of Joining,Date d'Embauche,
 Date of Joining must be greater than Date of Birth,La Date d'Embauche doit être après à la Date de Naissance,
 Date of Transaction,Date de transaction,
-Day,Jour,
 Debit,Débit,
 Debit ({0}),Débit ({0}),
 Debit Account,Compte de débit,
@@ -723,14 +674,12 @@
 Default BOM ({0}) must be active for this item or its template,Nomenclature par défaut ({0}) doit être actif pour ce produit ou son modèle,
 Default BOM for {0} not found,Nomenclature par défaut {0} introuvable,
 Default BOM not found for Item {0} and Project {1},La nomenclature par défaut n'a pas été trouvée pour l'Article {0} et le Projet {1},
-Default Letter Head,En-Tête de Courrier par Défaut,
 Default Tax Template,Modèle de Taxes par Défaut,
 Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM.,L’Unité de Mesure par Défaut pour l’Article {0} ne peut pas être modifiée directement parce que vous avez déjà fait une (des) transaction (s) avec une autre unité de mesure. Vous devez créer un nouvel article pour utiliser une UdM par défaut différente.,
 Default Unit of Measure for Variant '{0}' must be same as in Template '{1}',L’Unité de mesure par défaut pour la variante '{0}' doit être la même que dans le Modèle '{1}',
 Default settings for buying transactions.,Paramètres par défaut pour les transactions d'achat.,
 Default settings for selling transactions.,Paramètres par défaut pour les transactions de vente.,
 Default tax templates for sales and purchase are created.,Les modèles de taxe par défaut pour les ventes et les achats sont créés.,
-Defaults,Valeurs Par Défaut,
 Defense,Défense,
 Define Project type.,Définir le type de projet.,
 Define budget for a financial year.,Définir le budget pour un exercice.,
@@ -750,10 +699,8 @@
 Delivery Note {0} must not be submitted,Bon de Livraison {0} ne doit pas être soumis,
 Delivery Notes {0} must be cancelled before cancelling this Sales Order,Bons de Livraison {0} doivent être annulés avant d’annuler cette Commande Client,
 Delivery Notes {0} updated,Notes de livraison {0} mises à jour,
-Delivery Status,Statut de la Livraison,
 Delivery Trip,Service de Livraison,
 Delivery warehouse required for stock item {0},Entrepôt de Livraison requis pour article du stock {0},
-Department,Département,
 Department Stores,Grands magasins,
 Depreciation,Amortissement,
 Depreciation Amount,Montant d'Amortissement,
@@ -768,7 +715,6 @@
 Depreciation Row {0}: Next Depreciation Date cannot be before Purchase Date,Ligne d'amortissement {0}: la date d'amortissement suivante ne peut pas être antérieure à la date d'achat,
 Designer,Designer,
 Detailed Reason,Raison détaillée,
-Details,Détails,
 Details of Outward Supplies and inward supplies liable to reverse charge,Détails des livraisons sortantes et des livraisons entrantes susceptibles d'inverser la charge,
 Details of the operations carried out.,Détails des opérations effectuées.,
 Diagnosis,Diagnostique,
@@ -805,16 +751,11 @@
 Doc Name,Nom du document,
 Doc Type,Type de document,
 Docs Search,Recherche de documents,
-Document Name,Nom du Document,
-Document Type,Type de Document,
-Domain,Domaine,
-Domains,Domaines,
 Done,Terminé,
 Donor,Donneur,
 Donor Type information.,Informations sur le type de donneur.,
 Donor information.,Informations sur le donneur,
 Download JSON,Télécharger JSON,
-Draft,Brouillon,
 Drop Ship,Expédition Directe,
 Drug,Médicament,
 Due / Reference Date cannot be after {0},Date d’échéance / de référence ne peut pas être après le {0},
@@ -835,7 +776,6 @@
 ERPNext Settings,Paramètres ERPNext,
 Earliest,Au plus tôt,
 Earnest Money,Arrhes,
-Edit,modifier,
 Edit Publishing Details,Modifier les détails de publication,
 "Edit in full page for more options like assets, serial nos, batches etc.","Modifier en pleine page pour plus d'options comme les actifs, les numéros de série, les lots, etc.",
 Education,Éducation,
@@ -846,13 +786,9 @@
 Electronic Equipments,Équipements électroniques,
 Electronics,Électronique,
 Eligible ITC,CTI éligible,
-Email Account,Compte Email,
-Email Address,Adresse électronique,
 "Email Address must be unique, already exists for {0}","Adresse Email doit être unique, existe déjà pour {0}",
 Email Digest: ,Compte Rendu par Email :,
 Email Reminders will be sent to all parties with email contacts,Les rappels par emails seront envoyés à toutes les parties avec des contacts ayant une adresse email,
-Email Sent,Email Envoyé,
-Email Template,Modèle d'email,
 Email not found in default contact,Email non trouvé dans le contact par défaut,
 Email sent to {0},Email envoyé à {0},
 Employee,Employé,
@@ -866,9 +802,7 @@
 Employee {0} has already applied for {1} between {2} and {3} : ,L'employé {0} a déjà postulé pour {1} entre {2} et {3}:,
 Employee {0} of grade {1} have no default leave policy,L'employé {0} avec l'échelon {1} n'a pas de politique de congé par défaut,
 Enable / disable currencies.,Activer / Désactiver les devises,
-Enabled,Activé,
 "Enabling 'Use for Shopping Cart', as Shopping Cart is enabled and there should be at least one Tax Rule for Shopping Cart","Activation de 'Utiliser pour Panier', comme le Panier est activé et qu'il devrait y avoir au moins une Règle de Taxes pour le Panier",
-End Date,Date de Fin,
 End Date can not be less than Start Date,La date de fin ne peut être inférieure à la date de début,
 End Date cannot be before Start Date.,La date de fin ne peut pas être antérieure à la date de début.,
 End Year,Année de Fin,
@@ -889,7 +823,6 @@
 Entertainment & Leisure,Divertissement et Loisir,
 Entertainment Expenses,Charges de Représentation,
 Equity,Capitaux Propres,
-Error Log,Journal des Erreurs,
 Error evaluating the criteria formula,Erreur lors de l'évaluation de la formule du critère,
 Error in formula or condition: {0},Erreur dans la formule ou dans la condition : {0},
 Error: Not a valid id?,Erreur : Pas un identifiant valide ?,
@@ -901,7 +834,6 @@
 Excise Invoice,Facture d'Accise,
 Execution,Exécution,
 Executive Search,Recrutement de Cadres,
-Expand All,Développer Tout,
 Expected Delivery Date,Date de livraison prévue,
 Expected Delivery Date should be after Sales Order Date,La Date de Livraison Prévue doit être après la Date indiquée sur la Commande Client,
 Expected End Date,Date de fin prévue,
@@ -924,30 +856,22 @@
 Export E-Invoices,Exporter des factures électroniques,
 Extra Large,Extra large,
 Extra Small,Très Petit,
-Fail,Échec,
-Failed,Échoué,
 Failed to create website,Échec de la création du site Web,
 Failed to install presets,Échec de l'installation des préréglages,
 Failed to login,Échec de la connexion,
 Failed to setup company,Échec de la configuration de la société,
 Failed to setup defaults,Échec de la configuration par défaut,
 Failed to setup post company fixtures,Échec de la configuration des éléments liés la société,
-Fax,Fax,
 Fee,Frais,
 Fee Created,Honoraires Créés,
 Fee Creation Failed,La création des honoraires a échoué,
 Fee Creation Pending,Création d'honoraires en attente,
 Fee Records Created - {0},Archive d'Honoraires Créée - {0},
-Feedback,Retour d’Expérience,
 Fees,Honoraires,
-Female,Féminin,
 Fetch Data,Récupérer des données,
 Fetch Subscription Updates,Vérifier les mises à jour des abonnements,
 Fetch exploded BOM (including sub-assemblies),Récupérer la nomenclature éclatée (y compris les sous-ensembles),
 Fetching records......,Récupération des enregistrements ......,
-Field Name,Nom du Champ,
-Fieldname,Nom du Champ,
-Fields,Champ,
 "Filter Fields Row #{0}: Fieldname <b>{1}</b> must be of type ""Link"" or ""Table MultiSelect""",Filtrer les champs Ligne # {0}: le nom de champ <b>{1}</b> doit être de type &quot;Lien&quot; ou &quot;Table MultiSelect&quot;,
 Filter Total Zero Qty,Filtrer les totaux pour les qtés égales à zéro,
 Finance Book,Livre comptable,
@@ -961,7 +885,6 @@
 Finished Goods,Produits finis,
 Finished Item {0} must be entered for Manufacture type entry,Le Produit Fini {0} doit être saisi pour une écriture de type Production,
 Finished product quantity <b>{0}</b> and For Quantity <b>{1}</b> cannot be different,La quantité de produit fini <b>{0}</b> et Pour la quantité <b>{1}</b> ne peut pas être différente,
-First Name,Prénom,
 "Fiscal Regime is mandatory, kindly set the fiscal regime in the company {0}","Le régime fiscal est obligatoire, veuillez définir le régime fiscal de l'entreprise {0}",
 Fiscal Year,Exercice fiscal,
 Fiscal Year End Date should be one year after Fiscal Year Start Date,La date de fin d'exercice doit être un an après la date de début d'exercice,
@@ -994,9 +917,6 @@
 Forum Activity,Activité du forum,
 Free item code is not selected,Le code d'article gratuit n'est pas sélectionné,
 Freight and Forwarding Charges,Frais de Fret et d'Expédition,
-Frequency,Fréquence,
-Friday,Vendredi,
-From,À partir de,
 From Address 1,Ligne d'addresse 1 (Origine),
 From Address 2,Ligne d'addresse 2 (Origine),
 From Currency and To Currency cannot be same,La Devise de Base et la Devise de Cotation ne peuvent pas identiques,
@@ -1021,18 +941,15 @@
 From value must be less than to value in row {0},De la valeur doit être inférieure à la valeur de la ligne {0},
 From {0} | {1} {2},Du {0} | {1} {2},
 Fulfillment,Livraison,
-Full Name,Nom Complet,
 Fully Depreciated,Complètement Déprécié,
 Furnitures and Fixtures,Meubles et Accessoires,
 "Further accounts can be made under Groups, but entries can be made against non-Groups","D'autres comptes individuels peuvent être créés dans les groupes, mais les écritures ne peuvent être faites que sur les comptes individuels",
 Further cost centers can be made under Groups but entries can be made against non-Groups,"D'autres centres de coûts peuvent être créés dans des Groupes, mais des écritures ne peuvent être faites que sur des centres de coûts individuels.",
-Further nodes can be only created under 'Group' type nodes,D'autres nœuds peuvent être créés uniquement sous les nœuds de type 'Groupe',
 GSTIN,GSTIN,
 GSTR3B-Form,GSTR3B-Form,
 Gain/Loss on Asset Disposal,Gain/Perte sur Cessions des Immobilisations,
 Gantt Chart,Diagramme de Gantt,
 Gantt chart of all tasks.,Diagramme de Gantt de toutes les tâches.,
-Gender,Sexe,
 General,Général,
 General Ledger,Grand Livre,
 Generate Material Requests (MRP) and Work Orders.,Générer des demandes de matériel (MRP) et des ordres de travail.,
@@ -1050,7 +967,6 @@
 Get customers from,Obtenir les clients de,
 Get from Patient Encounter,Obtenez de la rencontre du patient,
 Getting Started,Commencer,
-GitHub Sync ID,GitHub Sync ID,
 Global settings for all manufacturing processes.,Paramètres globaux pour tous les processus de production.,
 Go to the Desktop and start using ERPNext,Accédez au bureau et commencez à utiliser ERPNext,
 GoCardless SEPA Mandate,Mandat SEPA GoCardless,
@@ -1090,8 +1006,6 @@
 HR Manager,Responsable RH,
 HSN,HSN,
 HSN/SAC,HSN / SAC,
-Half Yearly,Semestriel,
-Half-Yearly,Semestriel,
 Hardware,Matériel,
 Head of Marketing and Sales,Responsable du Marketing et des Ventes,
 Health Care,Soins de santé,
@@ -1106,7 +1020,6 @@
 Healthcare Services,Services de santé,
 Healthcare Settings,Paramètres de santé,
 Help Results for,Aide Résultats pour,
-High,Haut,
 High Sensitivity,Haute sensibilité,
 Hold,Mettre en attente,
 Hold Invoice,Facture en attente,
@@ -1114,15 +1027,12 @@
 Holiday List,Liste de vacances,
 Hotel Rooms of type {0} are unavailable on {1},Les chambres d'hôtel de type {0} sont indisponibles le {1},
 Hotels,Hôtels,
-Hourly,Horaire,
 Hours,Heures,
 How Pricing Rule is applied?,Comment la Règle de Prix doit-elle être appliquée ?,
 Hub Category,Catégorie du Hub,
-Hub Sync ID,Hub Sync ID,
 Human Resource,Ressource humaine,
 Human Resources,Ressources humaines,
 IGST Amount,IGST Montant,
-IP Address,Adresse IP,
 ITC Available (whether in full op part),CIT Disponible (que ce soit en partie op),
 ITC Reversed,CTI inversé,
 Identifying Decision Makers,Identifier les décideurs,
@@ -1133,11 +1043,7 @@
 "If unlimited expiry for the Loyalty Points, keep the Expiry Duration empty or 0.","Si vous souhaitez ne pas mettre de date d'expiration pour les points de fidélité, laissez la durée d'expiration vide ou mettez 0.",
 "If you have any questions, please get back to us.","Si vous avez des questions, veuillez revenir vers nous.",
 Ignore Existing Ordered Qty,Ignorer la quantité commandée existante,
-Image,Image,
-Image View,Voir l'Image,
-Import Data,Importer des données,
 Import Day Book Data,Données du journal d'importation,
-Import Log,Journal d'import,
 Import Master Data,Importer des données de base,
 Import in Bulk,Importer en Masse,
 Import of goods,Importation de marchandises,
@@ -1151,7 +1057,6 @@
 In Stock: ,En Stock :,
 In Value,En valeur,
 "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Dans le cas d'un programme à plusieurs échelons, les clients seront automatiquement affectés au niveau approprié en fonction de leurs dépenses",
-Inactive,Inactif,
 Incentives,Incitations,
 Include Default Book Entries,Inclure les entrées de livre par défaut,
 Include Exploded Items,Inclure les articles éclatés,
@@ -1185,7 +1090,6 @@
 Inter-State Supplies,Fournitures inter-Etats,
 Internet Publishing,Publication Internet,
 Intra-State Supplies,Fournitures intra-étatiques,
-Introduction,Introduction,
 Invalid Attribute,Attribut invalide,
 Invalid Blanket Order for the selected Customer and Item,Commande avec limites non valide pour le client et l'article sélectionnés,
 Invalid Company for Inter Company Transaction.,Société non valide pour une transaction inter-sociétés.,
@@ -1217,8 +1121,6 @@
 Invoices for Costumers.,Factures pour les clients.,
 Inward supplies from ISD,Approvisionnement entrant de la DSI,
 Inward supplies liable to reverse charge (other than 1 & 2 above),Approvisionnements entrants susceptibles d’être dédouanés (autres que 1 et 2 ci-dessus),
-Is Active,Est Active,
-Is Default,Est Défaut,
 Is Existing Asset,Est Actif Existant,
 Is Frozen,Est gelé,
 Is Group,Est un Groupe,
@@ -1288,7 +1190,6 @@
 Journal Entries {0} are un-linked,Les Écritures de Journal {0} ne sont pas liées,
 Journal Entry,Écriture de Journal,
 Journal Entry {0} does not have account {1} or already matched against other voucher,L’Écriture de Journal {0} n'a pas le compte {1} ou est déjà réconciliée avec une autre pièce justificative,
-Kanban Board,Tableau Kanban,
 Key Reports,Rapports clés,
 LMS Activity,Activité LMS,
 Lab Test,Test de laboratoire,
@@ -1299,12 +1200,10 @@
 Lab Tests and Vital Signs,Tests de laboratoire et signes vitaux,
 Lab result datetime cannot be before testing datetime,La date et l'heure du résultat de laboratoire ne peuvent pas être avant la date et l'heure du test,
 Lab testing datetime cannot be before collection datetime,La date et l'heure du test de laboratoire ne peuvent pas être avant la date et l'heure de collecte,
-Label,Étiquette,
 Laboratory,Laboratoire,
 Large,Grand,
 Last Communication,Dernière communication,
 Last Communication Date,Date de la Dernière Communication,
-Last Name,Nom de Famille,
 Last Order Amount,Montant de la Dernière Commande,
 Last Order Date,Date de la dernière commande,
 Last Purchase Price,Dernier prix d'achat,
@@ -1326,9 +1225,7 @@
 Ledger,Livre,
 Legal,Juridique,
 Legal Expenses,Frais juridiques,
-Letter Head,En-Tête,
 Letter Heads for print templates.,En-Têtes pour les modèles d'impression.,
-Level,Niveau,
 Liability,Passif,
 Limit Crossed,Limite Dépassée,
 Link to Material Request,Lien vers la demande de matériel,
@@ -1343,7 +1240,6 @@
 Logs for maintaining sms delivery status,Journaux pour maintenir le statut de livraison des sms,
 Lost,Perdu,
 Lost Reasons,Raisons perdues,
-Low,Bas,
 Low Sensitivity,Faible sensibilité,
 Lower Income,Revenu bas,
 Loyalty Amount,Montant de fidélité,
@@ -1355,13 +1251,11 @@
 Main,Principal,
 Maintenance,Entretien,
 Maintenance Log,Journal de maintenance,
-Maintenance Manager,Responsable de Maintenance,
 Maintenance Schedule,Échéancier d'Entretien,
 Maintenance Schedule is not generated for all the items. Please click on 'Generate Schedule',L'Échéancier d'Entretien n'est pas créé pour tous les articles. Veuillez clicker sur 'Créer un Échéancier',
 Maintenance Schedule {0} exists against {1},Un Calendrier de Maintenance {0} existe pour {1},
 Maintenance Schedule {0} must be cancelled before cancelling this Sales Order,L'Échéancier d'Entretien {0} doit être annulé avant d'annuler cette Commande Client,
 Maintenance Status has to be Cancelled or Completed to Submit,Le statut de maintenance doit être annulé ou complété pour pouvoir être envoyé,
-Maintenance User,Maintenance Utilisateur,
 Maintenance Visit,Visite d'Entretien,
 Maintenance Visit {0} must be cancelled before cancelling this Sales Order,La Visite d'Entretien {0} doit être annulée avant d'annuler cette Commande Client,
 Maintenance start date can not be before delivery date for Serial No {0},La date de début d'entretien ne peut pas être antérieure à la date de livraison pour le N° de Série {0},
@@ -1369,7 +1263,6 @@
 Make Payment,Faire un Paiement,
 Make project from a template.,Faire un projet à partir d'un modèle.,
 Making Stock Entries,Faire des Écritures de Stock,
-Male,Masculin,
 Manage Customer Group Tree.,Gérer l'Arborescence des Groupes de Clients.,
 Manage Sales Partners.,Gérer les Partenaires Commerciaux.,
 Manage Sales Person Tree.,Gérer l'Arborescence des Vendeurs.,
@@ -1379,7 +1272,6 @@
 Manager,Directeur,
 Managing Projects,Gestion de Projets,
 Managing Subcontracting,Gestion de la Sous-traitance,
-Mandatory,Obligatoire,
 Mandatory field - Academic Year,Champ Obligatoire - Année Académique,
 Mandatory field - Get Students From,Champ Obligatoire - Obtenir des étudiants de,
 Mandatory field - Program,Champ obligatoire - Programme,
@@ -1388,8 +1280,6 @@
 Manufacturer Part Number,Numéro de Pièce du Fabricant,
 Manufacturing,Production,
 Manufacturing Quantity is mandatory,Quantité de production obligatoire,
-Mapping,Mapping,
-Mapping Type,Type de Mapping,
 Mark Absent,Marquer Absent,
 Mark Half Day,Marquer Demi-Journée,
 Mark Present,Marquer Présent,
@@ -1424,7 +1314,6 @@
 Medical Code Standard,Standard du code médical,
 Medical Department,Département médical,
 Medical Record,Dossier médical,
-Medium,Moyen,
 Member Activity,Activité des membres,
 Member ID,ID du membre,
 Member Name,Nom de membre,
@@ -1439,12 +1328,8 @@
 Merge Account,Fusionner le compte,
 Merge with Existing Account,Fusionner avec un compte existant,
 "Merging is only possible if following properties are same in both records. Is Group, Root Type, Company","La combinaison est possible seulement si les propriétés suivantes sont les mêmes dans les deux dossiers. Est Groupe, Type de Racine, Société",
-Message Examples,Exemples de Messages,
 Message Sent,Message envoyé,
-Method,Méthode,
 Middle Income,Revenu Intermédiaire,
-Middle Name,Deuxième Nom,
-Middle Name (Optional),Deuxième Prénom (Optionnel),
 Min Amt can not be greater than Max Amt,Min Amt ne peut pas être supérieur à Max Amt,
 Min Qty can not be greater than Max Qty,Qté Min ne peut pas être supérieure à Qté Max,
 Minimum Lead Age (Days),Âge Minimum du lead (Jours),
@@ -1458,14 +1343,9 @@
 Mode of Transportation,Mode de transport,
 Model,Modèle,
 Moderate Sensitivity,Sensibilité modérée,
-Monday,Lundi,
-Monthly,Mensuel,
 Monthly Distribution,Répartition Mensuelle,
-More,Plus,
-More Information,Informations Complémentaires,
 More...,Plus...,
 Motion Picture & Video,Cinéma & Vidéo,
-Move,mouvement,
 Move Item,Déplacer l'Article,
 Multi Currency,Multi-devise,
 Multiple Item prices.,Plusieurs Prix d'Articles.,
@@ -1497,7 +1377,6 @@
 Net Profit,Bénéfice net,
 Net Total,Total net,
 New Account Name,Nouveau Nom de Compte,
-New Address,Nouvelle adresse,
 New BOM,Nouvelle nomenclature,
 New Batch ID (Optional),Nouveau Numéro de Lot (Optionnel),
 New Batch Qty,Nouvelle Qté de Lot,
@@ -1517,13 +1396,11 @@
 New task,Nouvelle tâche,
 New {0} pricing rules are created,De nouvelles règles de tarification {0} sont créées.,
 Newspaper Publishers,Éditeurs de journaux,
-Next,Suivant,
 Next Contact By cannot be same as the Lead Email Address,Prochain Contact Par ne peut être identique à l’Adresse Email du Lead,
 Next Contact Date cannot be in the past,La Date de Prochain Contact ne peut pas être dans le passé,
 Next Steps,Prochaines étapes,
 No Action,Pas d'action,
 No Customers yet!,Pas encore de clients!,
-No Data,Aucune Donnée,
 No Delivery Note selected for Customer {},Aucun bon de livraison sélectionné pour le client {},
 No Item with Barcode {0},Aucun Article avec le Code Barre {0},
 No Item with Serial No {0},Aucun Article avec le N° de Série {0},
@@ -1565,15 +1442,12 @@
 Non Profit (beta),Association (bêta),
 Non-GST outward supplies,Fournitures sortantes non liées à la TPS,
 Non-Group to Group,Non-Groupe à Groupe,
-None,Aucun,
 None of the items have any change in quantity or value.,Aucun des Articles n’a de changement en quantité ou en valeur.,
 Nos,N°,
 Not Available,Indisponible,
 Not Marked,Non marqué,
 Not Paid and Not Delivered,Non payé et non livré,
-Not Permitted,Non Autorisé,
 Not Started,Non Commencé,
-Not active,Non actif,
 Not allow to set alternative item for the item {0},Ne permet pas de définir un autre article pour l'article {0},
 Not allowed to update stock transactions older than {0},Non autorisé à mettre à jour les transactions du stock antérieures à {0},
 Not authorized to edit frozen Account {0},Vous n'êtes pas autorisé à modifier le compte gelé {0},
@@ -1592,7 +1466,6 @@
 Nothing is included in gross,Rien n'est inclus dans le brut,
 Nothing more to show.,Rien de plus à montrer.,
 Notify Customers via Email,Avertir les clients par courrier électronique,
-Number,Nombre,
 Number of Depreciations Booked cannot be greater than Total Number of Depreciations,Nombre d’Amortissements Comptabilisés ne peut pas être supérieur à Nombre Total d'Amortissements,
 Number of Interaction,Nombre d'Interactions,
 Number of Order,Nombre de Commandes,
@@ -1634,7 +1507,6 @@
 Opening Stock Balance,Solde d'Ouverture des Stocks,
 Opening Value,Valeur d'Ouverture,
 Opening {0} Invoice created,Ouverture {0} Facture créée,
-Operation,Opération,
 Operation Time must be greater than 0 for Operation {0},Temps de l'Opération doit être supérieur à 0 pour l'Opération {0},
 "Operation {0} longer than any available working hours in workstation {1}, break down the operation into multiple operations","Opération {0} plus longue que toute heure de travail disponible dans la station de travail {1}, veuillez séparer l'opération en plusieurs opérations",
 Operations,Opérations,
@@ -1647,7 +1519,6 @@
 Opportunity Amount,Montant de l'opportunité,
 "Optional. Sets company's default currency, if not specified.","Optionnel. Défini la devise par défaut de l'entreprise, si non spécifié.",
 Optional. This setting will be used to filter in various transactions.,Facultatif. Ce paramètre sera utilisé pour filtrer différentes transactions.,
-Options,Options,
 Order Count,Compte de Commandes,
 Order Entry,Saisie de Commande,
 Order Value,Valeur de la commande,
@@ -1660,9 +1531,9 @@
 Orders released for production.,Commandes validées pour la production.,
 Organization,Organisation,
 Organization Name,Nom de l'Organisation,
-Other,Autre,
 Other Reports,Autres rapports,
 "Other outward supplies(Nil rated,Exempted)","Autres livraisons sortantes (cotations nulles, exemptées)",
+Others,Autres,
 Out Qty,Qté Sortante,
 Out Value,Valeur Sortante,
 Out of Order,Hors service,
@@ -1676,7 +1547,6 @@
 Overdue,En retard,
 Overlap in scoring between {0} and {1},Chevauchement dans la notation entre {0} et {1},
 Overlapping conditions found between:,Conditions qui coincident touvées entre :,
-Owner,Responsable,
 PAN,Numéro de compte permanent (PAN),
 POS,PDV,
 POS Profile,Profil PDV,
@@ -1691,7 +1561,6 @@
 Paid Amount cannot be greater than total negative outstanding amount {0},Le Montant Payé ne peut pas être supérieur au montant impayé restant {0},
 Paid amount + Write Off Amount can not be greater than Grand Total,Le Montant Payé + Montant Repris ne peut pas être supérieur au Total Général,
 Paid and Not Delivered,Payé et non livré,
-Parameter,Paramètre,
 Parent Item {0} must not be a Stock Item,L'Article Parent {0} ne doit pas être un Élément de Stock,
 Parents Teacher Meeting Attendance,Participation à la réunion parents-professeurs,
 Partially Depreciated,Partiellement déprécié,
@@ -1751,7 +1620,6 @@
 Pension Funds,Fonds de Pension,
 Percentage Allocation should be equal to 100%,Pourcentage d'Allocation doit être égale à 100 %,
 Perception Analysis,Analyse de perception,
-Period,Période,
 Period Closing Entry,Écriture de Clôture de la Période,
 Period Closing Voucher,Bon de Clôture de la Période,
 Periodicity,Périodicité,
@@ -1778,7 +1646,6 @@
 Please define grade for Threshold 0%,Veuillez définir une note pour le Seuil 0%,
 Please enable Applicable on Booking Actual Expenses,Veuillez activer l'option : Applicable sur la base de l'enregistrement des dépenses réelles,
 Please enable Applicable on Purchase Order and Applicable on Booking Actual Expenses,Veuillez activer les options : Applicable sur la base des bons de commande d'achat et Applicable sur la base des bons de commande d'achat,
-Please enable pop-ups,Veuillez autoriser les pop-ups,
 Please enter 'Is Subcontracted' as Yes or No,Veuillez entrer Oui ou Non pour 'Est sous-traitée',
 Please enter API Consumer Key,"Veuillez entrer la clé ""API Consumer Key""",
 Please enter API Consumer Secret,"Veuillez entrer la clé ""API Consumer Secret""",
@@ -1834,7 +1701,6 @@
 Please select BOM in BOM field for Item {0},Veuillez sélectionner une nomenclature dans le champ nomenclature pour l’Article {0},
 Please select Category first,Veuillez d’abord sélectionner une Catégorie,
 Please select Charge Type first,Veuillez d’abord sélectionner le Type de Facturation,
-Please select Company,Veuillez sélectionner une Société,
 Please select Company and Posting Date to getting entries,Veuillez sélectionner la société et la date de comptabilisation pour obtenir les écritures,
 Please select Company first,Veuillez d’abord sélectionner une Société,
 Please select Completion Date for Completed Asset Maintenance Log,Veuillez sélectionner la date d'achèvement pour le journal de maintenance des actifs terminé,
@@ -1875,7 +1741,6 @@
 Please select the assessment group other than 'All Assessment Groups',Sélectionnez un groupe d'évaluation autre que «Tous les Groupes d'Évaluation»,
 Please select the document type first,Veuillez d’abord sélectionner le type de document,
 Please select weekly off day,Veuillez sélectionnez les jours de congé hebdomadaires,
-Please select {0},Veuillez sélectionner {0},
 Please select {0} first,Veuillez d’abord sélectionner {0},
 Please set 'Apply Additional Discount On',Veuillez définir ‘Appliquer Réduction Supplémentaire Sur ‘,
 Please set 'Asset Depreciation Cost Center' in Company {0},Veuillez définir 'Centre de Coûts des Amortissements d’Actifs’ de la Société {0},
@@ -1941,7 +1806,6 @@
 Prescription Duration,Durée de la prescription,
 Prescriptions,Les prescriptions,
 Prev,Précédent,
-Preview,Aperçu,
 Previous Financial Year is not closed,L’Exercice Financier Précédent n’est pas fermé,
 Price,Prix,
 Price List,Liste de prix,
@@ -1959,13 +1823,11 @@
 Pricing Rules are further filtered based on quantity.,Les Règles de Tarification sont d'avantage filtrés en fonction de la quantité.,
 Primary Address Details,Détails de l'adresse principale,
 Primary Contact Details,Détails du contact principal,
-Print Format,Format d'Impression,
 Print IRS 1099 Forms,Imprimer les formulaires IRS 1099,
 Print Report Card,Imprimer le rapport,
 Print Settings,Paramètres d'impression,
 Print and Stationery,Impression et Papeterie,
 Print settings updated in respective print format,Paramètres d'impression mis à jour avec le format d'impression indiqué,
-Print taxes with zero amount,Impression de taxes avec un montant nul,
 Printing and Branding,Impression et Marque,
 Private Equity,Capital Investissement,
 Procedure,Procédure,
@@ -2012,15 +1874,12 @@
 Provisional Profit / Loss (Credit),Gain / Perte (Crédit) Provisoire,
 Publications,Des publications,
 Publish Items on Website,Publier les Articles sur le Site Web,
-Published,Publié,
 Publishing,Édition,
 Purchase,achat,
 Purchase Amount,Montant de l'Achat,
 Purchase Date,Date d'Achat,
 Purchase Invoice,Facture d’Achat,
 Purchase Invoice {0} is already submitted,La Facture d’Achat {0} est déjà soumise,
-Purchase Manager,Responsable des Achats,
-Purchase Master Manager,Responsable des Données d’Achats,
 Purchase Order,Commande d'Achat,
 Purchase Order Amount,Montant de la Commande d'Achat,
 Purchase Order Amount(Company Currency),Montant de la Commande d'Achat (devise de la société),
@@ -2035,7 +1894,6 @@
 Purchase Receipt,Reçu d’Achat,
 Purchase Receipt {0} is not submitted,Le Reçu d’Achat {0} n'est pas soumis,
 Purchase Tax Template,Modèle de Taxes pour les Achats,
-Purchase User,Utilisateur Acheteur,
 Purchase orders help you plan and follow up on your purchases,Les Bons de Commande vous aider à planifier et à assurer le suivi de vos achats,
 Purchasing,Achat,
 Purpose must be one of {0},L'Objet doit être parmi {0},
@@ -2065,7 +1923,6 @@
 Quantity to Manufacture must be greater than 0.,La quantité à produire doit être supérieur à 0.,
 Quantity to Produce,Quantité à produire,
 Quantity to Produce can not be less than Zero,La quantité à produire ne peut être inférieure à zéro,
-Query Options,Options de Requête,
 Queued for replacing the BOM. It may take a few minutes.,En file d'attente pour remplacer la nomenclature. Cela peut prendre quelques minutes.,
 Queued for updating latest price in all Bill of Materials. It may take a few minutes.,Mise à jour des prix les plus récents dans toutes les nomenclatures en file d'attente. Cela peut prendre quelques minutes.,
 Quick Journal Entry,Écriture Rapide dans le Journal,
@@ -2080,10 +1937,8 @@
 Quotations: ,Devis :,
 Quotes to Leads or Customers.,Devis de Lead ou Clients.,
 RFQs are not allowed for {0} due to a scorecard standing of {1},Les Appels d'Offres ne sont pas autorisés pour {0} en raison d'une note de {1} sur la fiche d'évaluation,
-Range,Plage,
 Rate,Prix,
 Rate:,Prix:,
-Rating,Évaluation,
 Raw Material,Matières Premières,
 Raw Materials,Matières premières,
 Raw Materials cannot be blank.,Matières Premières ne peuvent pas être vides.,
@@ -2099,35 +1954,25 @@
 Receipt document must be submitted,Le reçu doit être soumis,
 Receivable,Créance,
 Receivable Account,Compte Débiteur,
-Received,Reçu,
 Received On,Reçu le,
 Received Quantity,Quantité reçue,
 Received Stock Entries,Entrées de stock reçues,
 Receiver List is empty. Please create Receiver List,La Liste de Destinataires est vide. Veuillez créer une Liste de Destinataires,
-Recipients,Destinataires,
 Reconcile,Réconcilier,
 "Record of all communications of type email, phone, chat, visit, etc.","Enregistrement de toutes les communications de type email, téléphone, chat, visite, etc.",
 Records,Dossiers,
-Redirect URL,URL de Redirection,
 Ref,Ref,
 Ref Date,Date de Réf.,
-Reference,Référence,
 Reference #{0} dated {1},Référence #{0} datée du {1},
-Reference Date,Date de Référence,
 Reference Doctype must be one of {0},Doctype de la Référence doit être parmi {0},
-Reference Document,Document de Référence,
-Reference Document Type,Type du document de référence,
 Reference No & Reference Date is required for {0},N° et Date de Référence sont nécessaires pour {0},
 Reference No and Reference Date is mandatory for Bank transaction,Le N° de Référence et la Date de Référence sont nécessaires pour une Transaction Bancaire,
 Reference No is mandatory if you entered Reference Date,N° de Référence obligatoire si vous avez entré une date,
 Reference No.,Numéro de référence,
 Reference Number,Numéro de réference,
-Reference Type,Type de référence,
 "Reference: {0}, Item Code: {1} and Customer: {2}","Référence: {0}, Code de l'article: {1} et Client: {2}",
 References,Références,
-Refresh Token,Jeton de Rafraîchissement,
 Register,registre,
-Rejected,Rejeté,
 Related,en relation,
 Relation with Guardian1,Relation avec Tuteur1,
 Relation with Guardian2,Relation avec Tuteur2,
@@ -2139,17 +1984,12 @@
 Reminder to update GSTIN Sent,Rappel pour mettre à jour GSTIN envoyé,
 Remove item if charges is not applicable to that item,Retirer l'article si les charges ne lui sont pas applicables,
 Removed items with no change in quantity or value.,Les articles avec aucune modification de quantité ou de valeur ont étés retirés.,
-Reopen,Ré-ouvrir,
 Reorder Level,Niveau de réapprovisionnement,
 Reorder Qty,Qté de Réapprovisionnement,
 Repeat Customer Revenue,Revenus de Clients Récurrents,
 Repeat Customers,Clients Récurrents,
 Replace BOM and update latest price in all BOMs,Remplacer la nomenclature et actualiser les prix les plus récents dans toutes les nomenclatures,
-Replied,Répondu,
-Report,Rapport,
-Report Type,Type de Rapport,
 Report Type is mandatory,Le Type de Rapport est nécessaire,
-Reports,Rapports,
 Reqd By Date,Requis par date,
 Reqd Qty,Qté obligatoire,
 Request for Quotation,Appel d'Offre,
@@ -2208,7 +2048,6 @@
 Root cannot have a parent cost center,Racine ne peut pas avoir un centre de coûts parent,
 Round Off,Arrondi,
 Rounded Total,Total arrondi,
-Route,Route,
 Row # {0}: ,Ligne # {0} :,
 Row # {0}: Batch No must be same as {1} {2},Ligne # {0} : Le N° de Lot doit être le même que {1} {2},
 Row # {0}: Cannot return more than {1} for Item {2},Ligne # {0} : Vous ne pouvez pas retourner plus de {1} pour l’Article {2},
@@ -2297,8 +2136,6 @@
 Sales Invoice,Facture de vente,
 Sales Invoice {0} has already been submitted,La Facture Vente {0} a déjà été transmise,
 Sales Invoice {0} must be cancelled before cancelling this Sales Order,Facture de Vente {0} doit être annulée avant l'annulation de cette Commande Client,
-Sales Manager,Responsable des Ventes,
-Sales Master Manager,Directeur des Ventes,
 Sales Order,Commande client,
 Sales Order Item,Article de la Commande Client,
 Sales Order required for Item {0},Commande Client requise pour l'Article {0},
@@ -2314,11 +2151,9 @@
 Sales Summary,Récapitulatif des ventes,
 Sales Tax Template,Modèle de la Taxe de Vente,
 Sales Team,Équipe des Ventes,
-Sales User,Chargé de Ventes,
 Sales and Returns,Ventes et retours,
 Sales campaigns.,Campagnes de vente.,
 Sales orders are not available for production,Aucune commande client n'est disponible pour la production,
-Salutation,Salutations,
 Same Company is entered more than once,La même Société a été entrée plus d'une fois,
 Same item cannot be entered multiple times.,Le même article ne peut pas être entré plusieurs fois.,
 Same supplier has been entered multiple times,Le même fournisseur a été saisi plusieurs fois,
@@ -2326,7 +2161,6 @@
 Sample quantity {0} cannot be more than received quantity {1},La quantité d'échantillon {0} ne peut pas dépasser la quantité reçue {1},
 Sanctioned,Sanctionné,
 Sand,Le sable,
-Saturday,Samedi,
 Saving {0},Enregistrement {0},
 Scan Barcode,Scan Code Barre,
 Schedule,Calendrier,
@@ -2334,18 +2168,15 @@
 Schedule Course,Cours Calendrier,
 Schedule Date,Date du Calendrier,
 Schedule Discharge,Décharge horaire,
-Scheduled,Prévu,
 Scheduled Upto,Programmé jusqu'à,
 "Schedules for {0} overlaps, do you want to proceed after skiping overlaped slots ?","Les plannings pour {0} se chevauchent, voulez-vous continuer sans prendre en compte les créneaux qui se chevauchent ?",
 Score cannot be greater than Maximum Score,Score ne peut pas être supérieure à Score maximum,
 Scorecards,Fiches d'Évaluation,
 Scrapped,Mis au rebut,
-Search,Rechercher,
 Search Results,Résultats de la recherche,
 Search Sub Assemblies,Rechercher les Sous-Ensembles,
 "Search by item code, serial number, batch no or barcode","Recherche par code article, numéro de série, numéro de lot ou code-barres",
 "Seasonality for setting budgets, targets etc.","Saisonnalité de l'établissement des budgets, des objectifs, etc.",
-Secret Key,Clef Secrète,
 Secretary,secrétaire,
 Section Code,Code de section,
 Secured Loans,Prêts garantis,
@@ -2355,7 +2186,6 @@
 See all open tickets,Voir tous les tickets ouverts,
 See past orders,Voir les commandes passées,
 See past quotations,Voir les citations passées,
-Select,Sélectionner,
 Select Alternate Item,Sélectionnez un autre élément,
 Select Attribute Values,Sélectionner les valeurs d'attribut,
 Select BOM,Sélectionner une nomenclature,
@@ -2369,7 +2199,6 @@
 Select Customer,Sélectionnez un client,
 Select Days,Choisissez des jours,
 Select Default Supplier,Sélectionner le Fournisseur par Défaut,
-Select DocType,Sélectionner le DocType,
 Select Fiscal Year...,Sélectionner Exercice ...,
 Select Item (optional),Sélectionnez l'Article (facultatif),
 Select Items based on Delivery Date,Sélectionnez les articles en fonction de la Date de Livraison,
@@ -2399,11 +2228,9 @@
 Selling Rate,Prix de vente,
 "Selling must be checked, if Applicable For is selected as {0}","Vente doit être vérifiée, si ""Applicable pour"" est sélectionné comme {0}",
 Send Grant Review Email,Envoyer un email d'examen de la demande de subvention,
-Send Now,Envoyer Maintenant,
 Send SMS,Envoyer un SMS,
 Send mass SMS to your contacts,Envoyer un SMS en masse à vos contacts,
 Sensitivity,Sensibilité,
-Sent,Envoyé,
 Serial No and Batch,N° de Série et lot,
 Serial No is mandatory for Item {0},N° de Série est obligatoire pour l'Article {0},
 Serial No {0} does not belong to Batch {1},Le numéro de série {0} n'appartient pas au lot {1},
@@ -2428,7 +2255,6 @@
 Series Updated,Série mise à jour,
 Series Updated Successfully,Mise à jour des Séries Réussie,
 Series is mandatory,Série est obligatoire,
-Service,Service,
 Service Level Agreement,Contrat de niveau de service,
 Service Level Agreement.,Contrat de niveau de service.,
 Service Level.,Niveau de service.,
@@ -2455,7 +2281,6 @@
 Setting up Employees,Configuration des Employés,
 Setting up Taxes,Configuration des Impôts,
 Setting up company,Création d'entreprise,
-Settings,Paramètres,
 "Settings for online shopping cart such as shipping rules, price list etc.","Paramètres du panier tels que les règles de livraison, liste de prix, etc.",
 Settings for website homepage,Paramètres de la page d'accueil du site,
 Settings for website product listing,Paramètres pour la liste de produits de sites Web,
@@ -2481,7 +2306,6 @@
 Shopify Supplier,Fournisseur Shopify,
 Shopping Cart,Panier,
 Shopping Cart Settings,Paramètres du panier,
-Short Name,Nom Court,
 Shortage Qty,Qté de Pénurie,
 Show Completed,Montrer terminé,
 Show Cumulative Amount,Afficher le montant cumulatif,
@@ -2500,7 +2324,6 @@
 Single Variant,Variante unique,
 Single unit of an Item.,Seule unité d'un Article.,
 "Skipping Leave Allocation for the following employees, as Leave Allocation records already exists against them. {0}","Attribution des congés de congé pour les employés suivants, car des dossiers de répartition des congés existent déjà contre eux. {0}",
-Slideshow,Diaporama,
 Slots for {0} are not added to the schedule,Les créneaux pour {0} ne sont pas ajoutés à l'agenda,
 Small,Petit,
 Soap & Detergent,Savons & Détergents,
@@ -2513,8 +2336,6 @@
 Some information is missing,Certaines informations sont manquantes,
 Something went wrong!,Quelque chose a mal tourné !,
 "Sorry, Serial Nos cannot be merged","Désolé, les N° de Série ne peut pas être fusionnés",
-Source,Source,
-Source Name,Nom de la Source,
 Source Warehouse,Entrepôt source,
 Source and Target Location cannot be same,Les localisations source et cible ne peuvent pas être identiques,
 Source and target warehouse cannot be same for row {0},L'entrepôt source et destination ne peuvent être similaire dans la ligne {0},
@@ -2529,14 +2350,12 @@
 Standard Buying,Achat standard,
 Standard Selling,Vente standard,
 Standard contract terms for Sales or Purchase.,Termes contractuels standards pour Ventes ou Achats,
-Start Date,Date de Début,
 Start Date of Agreement can't be greater than or equal to End Date.,La date de début de l'accord ne peut être supérieure ou égale à la date de fin.,
 Start Year,Année de début,
 Start date should be less than end date for Item {0},La date de début doit être antérieure à la date de fin pour l'Article {0},
 Start date should be less than end date for task {0},La date de début doit être inférieure à la date de fin de la tâche {0},
 Start day is greater than end day in task '{0}',La date de début est supérieure à la date de fin dans la tâche '{0}',
 Start on,Démarrer,
-State,Etat,
 State/UT Tax,Taxe Etat / UT,
 Statement of Account,Relevé de compte,
 Status must be one of {0},Le statut doit être l'un des {0},
@@ -2569,8 +2388,6 @@
 Stock cannot be updated against Purchase Receipt {0},Stock ne peut pas être mis à jour pour le Reçu d'Achat {0},
 Stock cannot exist for Item {0} since has variants,Stock ne peut pas exister pour l'Article {0} puisqu'il a des variantes,
 Stock transactions before {0} are frozen,Les transactions du stock avant {0} sont gelées,
-Stop,Arrêter,
-Stopped,Arrêté,
 "Stopped Work Order cannot be cancelled, Unstop it first to cancel","Un ordre de fabrication arrêté ne peut être annulé, Re-démarrez le pour pouvoir l'annuler",
 Stores,Magasins,
 Student,Étudiant,
@@ -2601,8 +2418,6 @@
 Sub Type,Sous type,
 Sub-contracting,Sous-traitant,
 Subcontract,Sous-traiter,
-Subject,Sujet,
-Submit,Valider,
 Submit this Work Order for further processing.,Valider cet ordre de fabrication pour continuer son traitement.,
 Subscription,Abonnement,
 Subscription Management,Gestion des abonnements,
@@ -2615,10 +2430,8 @@
 Successfully deleted all transactions related to this company!,Suppression de toutes les transactions liées à cette société avec succès !,
 Sum of Scores of Assessment Criteria needs to be {0}.,Somme des Scores de Critères d'Évaluation doit être {0}.,
 Sum of points for all goals should be 100. It is {0},Somme des points pour tous les objectifs devraient être 100. Il est {0},
-Summary,Résumé,
 Summary for this month and pending activities,Résumé du mois et des activités en suspens,
 Summary for this week and pending activities,Résumé de la semaine et des activités en suspens,
-Sunday,Dimanche,
 Suplier,Fournisseur,
 Supplier,Fournisseur,
 Supplier Group,Groupe de fournisseurs,
@@ -2648,20 +2461,15 @@
 Sync has been temporarily disabled because maximum retries have been exceeded,La synchronisation a été temporairement désactivée car les tentatives maximales ont été dépassées,
 Syntax error in condition: {0},Erreur de syntaxe dans la condition: {0},
 Syntax error in formula or condition: {0},Erreur de syntaxe dans la formule ou condition : {0},
-System Manager,Responsable Système,
 TDS Rate %,Pourcentage de TDS,
 Tap items to add them here,Choisissez des articles pour les ajouter ici,
-Target,Cible,
 Target ({}),Cible ({}),
 Target On,Cible sur,
 Target Warehouse,Entrepôt cible,
 Target warehouse is mandatory for row {0},L’Entrepôt cible est obligatoire pour la ligne {0},
-Task,Tâche,
-Tasks,Tâches,
 Tasks have been created for managing the {0} disease (on row {1}),Des tâches ont été créées pour gérer la maladie {0} (sur la ligne {1}),
 Tax,Taxe,
 Tax Assets,Actifs d'Impôts,
-Tax Category,Catégorie de taxe,
 Tax Category for overriding tax rates.,Catégorie de taxe pour les taux de taxe prépondérants.,
 "Tax Category has been changed to ""Total"" because all the Items are non-stock items","La Catégorie de Taxe a été changée à ""Total"" car tous les articles sont des articles hors stock",
 Tax ID,Numéro d'identification fiscale,
@@ -2769,11 +2577,9 @@
 Timesheets,Feuilles de temps,
 "Timesheets help keep track of time, cost and billing for activites done by your team","Les Feuilles de Temps aident au suivi du temps, coût et facturation des activités effectuées par votre équipe",
 Titles for print templates e.g. Proforma Invoice.,Titres pour les modèles d'impression e.g. Facture Proforma.,
-To,À,
 To Address 1,Ligne d'adresse 1 (Destination),
 To Address 2,Ligne d'adresse 2 (Destination),
 To Bill,À Facturer,
-To Date,Jusqu'au,
 To Date cannot be before From Date,La date de fin ne peut être antérieure à la date de début,
 To Date cannot be less than From Date,La date de fin ne peut pas précéder la date de début,
 To Date must be greater than From Date,La date de fin doit être supérieure à la date de début,
@@ -2809,6 +2615,7 @@
 Total (Without Tax),Total (hors taxes),
 Total Achieved,Total Obtenu,
 Total Actual,Total réel,
+Total Allocated Leaves,Total des congés alloués,
 Total Amount,Montant total,
 Total Amount Credited,Montant total crédité,
 Total Applicable Charges in Purchase Receipt Items table must be same as Total Taxes and Charges,Total des Frais Applicables dans la Table des Articles de Reçus d’Achat doit être égal au Total des Taxes et Frais,
@@ -2888,7 +2695,6 @@
 UOM,UdM,
 UOM Conversion factor is required in row {0},Facteur de conversion de l'UdM est obligatoire dans la ligne {0},
 UOM coversion factor required for UOM: {0} in Item: {1},Facteur de coversion UdM requis pour l'UdM : {0} dans l'Article : {1},
-URL,URL,
 Unable to find exchange rate for {0} to {1} for key date {2}. Please create a Currency Exchange record manually,Impossible de trouver le taux de change pour {0} à {1} pour la date clé {2}. Veuillez créer une entrée de taux de change manuellement,
 Unable to find score starting at {0}. You need to have standing scores covering 0 to 100,Impossible de trouver un score démarrant à {0}. Vous devez avoir des scores couvrant 0 à 100,
 Unable to find variable: ,Impossible de trouver une variable:,
@@ -2902,7 +2708,6 @@
 Unpaid,Impayé,
 Unsecured Loans,Prêts non garantis,
 Unsubscribe from this Email Digest,Se Désinscire de ce Compte Rendu par Email,
-Unsubscribed,Désinscrit,
 Until,Jusqu'à,
 Unverified Webhook Data,Données de Webhook non vérifiées,
 Update Account Name / Number,Mettre à jour le nom / numéro du compte,
@@ -2918,8 +2723,7 @@
 Upload your letter head and logo. (you can edit them later).,Charger votre en-tête et logo. (vous pouvez les modifier ultérieurement).,
 Upper Income,Revenu Élevé,
 Use Sandbox,Utiliser Sandbox,
-User,Utilisateur,
-User ID,Identifiant d'utilisateur,
+Used Leaves,Congés utilisés,
 User ID not set for Employee {0},ID de l'Utilisateur non défini pour l'Employé {0},
 User Remark,Remarque de l'Utilisateur,
 User has not applied rule on the invoice {0},L'utilisateur n'a pas appliqué la règle sur la facture {0},
@@ -2929,14 +2733,12 @@
 User {0} doesn't have any default POS Profile. Check Default at Row {1} for this User.,L'utilisateur {0} n'a aucun profil POS par défaut. Vérifiez par défaut à la ligne {1} pour cet utilisateur.,
 User {0} is already assigned to Employee {1},Utilisateur {0} est déjà attribué à l'Employé {1},
 User {0} is already assigned to Healthcare Practitioner {1},L'utilisateur {0} est déjà attribué à un professionnel de la santé {1},
-Users,Utilisateurs,
 Utility Expenses,Frais de Services d'Utilité Publique,
 Valid From Date must be lesser than Valid Upto Date.,La date de début de validité doit être inférieure à la date de mise en service valide.,
 Valid Till,Valable Jusqu'au,
 Valid from and valid upto fields are mandatory for the cumulative,Les champs valides à partir de et valables jusqu'à sont obligatoires pour le cumulatif.,
 Valid from date must be less than valid upto date,La date de début de validité doit être inférieure à la date de validité,
 Valid till date cannot be before transaction date,La date de validité ne peut pas être avant la date de transaction,
-Validity,Validité,
 Validity period of this quotation has ended.,La période de validité de ce devis a pris fin.,
 Valuation Rate,Taux de Valorisation,
 Valuation Rate is mandatory if Opening Stock entered,Le Taux de Valorisation est obligatoire si un Stock Initial est entré,
@@ -2991,7 +2793,6 @@
 Warehouses with child nodes cannot be converted to ledger,Les entrepôts avec nœuds enfants ne peuvent pas être convertis en livre,
 Warehouses with existing transaction can not be converted to group.,Les entrepôts avec des transactions existantes ne peuvent pas être convertis en groupe.,
 Warehouses with existing transaction can not be converted to ledger.,Les entrepôts avec des transactions existantes ne peuvent pas être convertis en livre.,
-Warning,Avertissement,
 Warning: Another {0} # {1} exists against stock entry {2},Attention : Un autre {0} {1} # existe pour l'écriture de stock {2},
 Warning: Invalid SSL certificate on attachment {0},Attention : certificat SSL non valide sur la pièce jointe {0},
 Warning: Invalid attachment {0},Attention : Pièce jointe non valide {0},
@@ -3001,16 +2802,9 @@
 Warranty,garantie,
 Warranty Claim,Réclamation de Garantie,
 Warranty Claim against Serial No.,Réclamation de Garantie pour le N° de Série.,
-Website,Site Web,
 Website Image should be a public file or website URL,L'Image du Site Web doit être un fichier public ou l'URL d'un site web,
 Website Image {0} attached to Item {1} cannot be found,Image pour le Site Web {0} attachée à l'Article {1} ne peut pas être trouvée,
-Website Manager,Responsable du Site Web,
-Website Settings,Paramètres du Site web,
-Wednesday,Mercredi,
-Week,Semaine,
-Weekly,Hebdomadaire,
 "Weight is mentioned,\nPlease mention ""Weight UOM"" too","Poids est mentionné,\nVeuillez aussi mentionner ""UdM de Poids""",
-Welcome email sent,Email de bienvenue envoyé,
 Welcome to ERPNext,Bienvenue sur ERPNext,
 What do you need help with?,Avec quoi avez vous besoin d'aide ?,
 What does it do?,Qu'est-ce que ça fait ?,
@@ -3073,9 +2867,7 @@
 "e.g. ""Build tools for builders""","e.g. ""Construire des outils pour les constructeurs""",
 "e.g. ""Primary School"" or ""University""","e.g. ""École Primaire"" ou ""Université""",
 "e.g. Bank, Cash, Credit Card","e.g. Cash, Banque, Carte de crédit",
-hidden,Masqué,
 modified,modifié,
-old_parent,grand_parent,
 on,sur,
 {0} '{1}' is disabled,{0} '{1}' est désactivé(e),
 {0} '{1}' not in Fiscal Year {2},{0} '{1}' n'est pas dans l’Exercice {2},
@@ -3108,7 +2900,6 @@
 {0} hours,{0} heures,
 {0} in row {1},{0} dans la ligne {1},
 {0} is blocked so this transaction cannot proceed,{0} est bloqué donc cette transaction ne peut pas continuer,
-{0} is mandatory,{0} est obligatoire,
 {0} is mandatory for Item {1},{0} est obligatoire pour l’Article {1},
 {0} is mandatory. Maybe Currency Exchange record is not created for {1} to {2}.,{0} est obligatoire. Peut-être qu’un enregistrement de Taux de Change n'est pas créé pour {1} et {2}.,
 {0} is not a stock Item,{0} n'est pas un Article de stock,
@@ -3168,37 +2959,8 @@
 {0}: {1} does not exists,{0} : {1} n’existe pas,
 {0}: {1} not found in Invoice Details table,{0} : {1} introuvable dans la table de Détails de la Facture,
 {} of {},{} de {},
-Assigned To,Assigné À,
-Chat,Chat,
 Completed By,Effectué par,
-Day of Week,Jour de la semaine,
-"Dear System Manager,","Cher Administrateur Système ,",
-Default Value,Valeur par Défaut,
-Email Group,Groupe Email,
-Email Settings,Paramètres d'Email,
-Email not sent to {0} (unsubscribed / disabled),Email pas envoyé à {0} (désabonné / désactivé),
-Error Message,Message d'erreur,
-Fieldtype,Type de Champ,
-Help Articles,Articles d'Aide,
-ID,ID,
-Import,Importer,
-Language,Langue,
-Likes,Aime,
-Merge with existing,Fusionner avec existant,
-Orientation,Orientation,
-Parent,Parent,
 Payment Failed,Le Paiement a Échoué,
-Personal,Personnel,
-Post,Poster,
-Postal Code,code postal,
-Provider,Fournisseur,
-Read Only,Lecture Seule,
-Recipient,Destinataire,
-Reviews,Avis,
-Sender,Expéditeur,
-There were errors while sending email. Please try again.,Il y a eu des erreurs lors de l'envoi d’emails. Veuillez essayer à nouveau.,
-Values Changed,Valeurs Modifiées,
-or,ou,
 Ageing Range 4,Gamme de vieillissement 4,
 Allocated amount cannot be greater than unadjusted amount,Le montant alloué ne peut être supérieur au montant non ajusté,
 Allocated amount cannot be negative,Le montant alloué ne peut être négatif,
@@ -3224,22 +2986,7 @@
 Show {0},Montrer {0},
 Target Details,Détails de la cible,
 {0} already has a Parent Procedure {1}.,{0} a déjà une procédure parent {1}.,
-API,API,
-Annual,Annuel,
-Change,Changement,
-Contact Email,Email du Contact,
-From Date,A partir du,
-Group By,Par groupe,
-Invalid URL,URL invalide,
-Landscape,Paysage,
-Naming Series,Masque de numérotation,
-No data to export,Aucune donnée à exporter,
-Portrait,Portrait,
 Print Heading,Imprimer Titre,
-Scheduler Inactive,Planificateur inactif,
-Scheduler is inactive. Cannot import data.,Le planificateur est inactif. Impossible d'importer des données.,
-Show Document,Afficher le document,
-Show Traceback,Afficher le traçage,
 Video,Vidéo,
 % Of Grand Total,% Du grand total,
 <b>Company</b> is a mandatory filter.,<b>La société</b> est un filtre obligatoire.,
@@ -3257,20 +3004,11 @@
 Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}.,La dimension de comptabilité <b>{0}</b> est requise pour le compte 'Bénéfices et pertes' {1}.,
 Accounting Masters,Maîtres Comptables,
 Accounting Period overlaps with {0},La période comptable chevauche avec {0},
-Activity,Activité,
-Add / Manage Email Accounts.,Ajouter / Gérer les Comptes de Messagerie.,
-Add Child,Ajouter une Sous-Catégorie,
-Add Multiple,Ajout Multiple,
-Add Participants,Ajouter des participants,
 Add to Featured Item,Ajouter à l'article en vedette,
 Add your review,Ajouter votre avis,
 Add/Edit Coupon Conditions,Ajouter / Modifier les conditions du coupon,
 Added to Featured Items,Ajouté aux articles en vedette,
-Added {0} ({1}),Ajouté {0} ({1}),
-Address Line 1,Adresse Ligne 1,
-Addresses,Adresses,
 Admission End Date should be greater than Admission Start Date.,La date de fin d'admission doit être supérieure à la date de début d'admission.,
-All,Tout,
 All bank transactions have been created,Toutes les transactions bancaires ont été créées,
 All the depreciations has been booked,Toutes les amortissements ont été comptabilisés,
 Allow Resetting Service Level Agreement from Support Settings.,Autoriser la réinitialisation du contrat de niveau de service à partir des paramètres de support.,
@@ -3305,17 +3043,12 @@
 Batch no is required for batched item {0},Le numéro de lot est requis pour l'article en lot {0}.,
 Billing Date,Date de facturation,
 Billing Interval Count cannot be less than 1,Le nombre d'intervalles de facturation ne peut pas être inférieur à 1,
-Blue,Bleu,
-Book,Livre,
 Book Appointment,Prendre rendez-vous,
-Brand,Marque,
-Browse,Feuilleter,
 Call Connected,Appel connecté,
 Call Disconnected,Appel déconnecté,
 Call Missed,Appel manqué,
 Call Summary,Résumé d'appel,
 Call Summary Saved,Résumé de l'appel enregistré,
-Cancelled,Annulé,
 Cannot Calculate Arrival Time as Driver Address is Missing.,Impossible de calculer l'heure d'arrivée car l'adresse du conducteur est manquante.,
 Cannot Optimize Route as Driver Address is Missing.,Impossible d'optimiser l'itinéraire car l'adresse du pilote est manquante.,
 Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.,Impossible de terminer la tâche {0} car sa tâche dépendante {1} n'est pas terminée / annulée.,
@@ -3324,26 +3057,19 @@
 "Capacity Planning Error, planned start time can not be same as end time","Erreur de planification de capacité, l'heure de début prévue ne peut pas être identique à l'heure de fin",
 Categories,Catégories,
 Changes in {0},Changements dans {0},
-Chart,Graphique,
 Choose a corresponding payment,Choisissez un paiement correspondant,
 Click on the link below to verify your email and confirm the appointment,Cliquez sur le lien ci-dessous pour vérifier votre email et confirmer le rendez-vous,
-Close,Fermer,
 Communication,Communication,
 Compact Item Print,Impression de l'Article Compacté,
-Company,Société,
 Company of asset {0} and purchase document {1} doesn't matches.,La société de l'actif {0} et le document d'achat {1} ne correspondent pas.,
 Compare BOMs for changes in Raw Materials and Operations,Comparer les nomenclatures aux modifications apportées aux matières premières et aux opérations,
 Compare List function takes on list arguments,La fonction de comparaison de liste accepte les arguments de liste,
-Complete,Terminé,
-Completed,Terminé,
 Completed Quantity,Quantité terminée,
 Connect your Exotel Account to ERPNext and track call logs,Connectez votre compte Exotel à ERPNext et suivez les journaux d'appels,
 Connect your bank accounts to ERPNext,Connectez vos comptes bancaires à ERPNext,
 Contact Seller,Contacter le vendeur,
-Continue,Continuer,
 Cost Center: {0} does not exist,Centre de coûts: {0} n'existe pas,
 Couldn't Set Service Level Agreement {0}.,Impossible de définir le contrat de service {0}.,
-Country,Pays,
 Country Code in File does not match with country code set up in the system,Le code de pays dans le fichier ne correspond pas au code de pays configuré dans le système,
 Create New Contact,Créer un nouveau contact,
 Create New Lead,Créer une nouvelle lead,
@@ -3354,34 +3080,20 @@
 Credit limit is already defined for the Company {0},La limite de crédit est déjà définie pour la société {0}.,
 Ctrl + Enter to submit,Ctrl + Entrée pour valider,
 Ctrl+Enter to submit,Ctrl + Entrée pour valider,
-Currency,Devise,
-Current Status,Statut Actuel,
 Customer PO,Commande d'Achat client,
-Daily,Quotidien,
-Date,Date,
 Date of Birth cannot be greater than Joining Date.,La date de naissance ne peut pas être supérieure à la date d'adhésion.,
-Dear,Cher/Chère,
-Default,Par Défaut,
 Define coupon codes.,Définissez les codes promo.,
 Delayed Days,Jours retardés,
-Delete,Supprimer,
 Delivered Quantity,Quantité livrée,
 Delivery Notes,Bons de livraison,
 Depreciated Amount,Montant amorti,
-Description,Description,
-Designation,Désignation,
 Difference Value,Valeur de différence,
 Dimension Filter,Filtre de dimension,
-Disabled,Desactivé,
 Disbursement and Repayment,Décaissement et remboursement,
 Distance cannot be greater than 4000 kms,La distance ne peut pas dépasser 4000 km,
 Do you want to submit the material request,Voulez-vous valider la demande de matériel,
-Doctype,Doctype,
 Document {0} successfully uncleared,Document {0} non effacé avec succès,
-Download Template,Télécharger le Modèle,
 Dr,Dr,
-Due Date,Date d'Échéance,
-Duplicate,Dupliquer,
 Duplicate Project with Tasks,Projet en double avec tâches,
 Duplicate project has been created,Un projet en double a été créé,
 E-Way Bill JSON can only be generated from a submitted document,E-Way Bill JSON ne peut être généré qu'à partir d'un document soumis,
@@ -3391,7 +3103,6 @@
 Earliest Age,Âge le plus précoce,
 Edit Details,Modifier les détails,
 Either GST Transporter ID or Vehicle No is required if Mode of Transport is Road,Un numéro d'identification de transporteur ou un numéro de véhicule est requis si le mode de transport est la route.,
-Email,Email,
 Email Campaigns,Campagnes de courrier électronique,
 Employee ID is linked with another instructor,L'ID de l'employé est lié à un autre instructeur,
 Employee Tax and Benefits,Impôt et avantages sociaux des employés,
@@ -3403,21 +3114,13 @@
 Energy Point Leaderboard,Point de classement énergétique,
 Enter API key in Google Settings.,Entrez la clé API dans les paramètres Google.,
 Enter Supplier,Entrez le fournisseur,
-Enter Value,Entrez une Valeur,
-Entity Type,Type d'entité,
-Error,Erreur,
 Error in Exotel incoming call,Erreur dans un appel entrant Exotel,
 Error: {0} is mandatory field,Erreur: {0} est un champ obligatoire,
 Exception occurred while reconciling {0},Une exception s'est produite lors de la réconciliation {0},
 Expected and Discharge dates cannot be less than Admission Schedule date,Les dates prévues et de sortie ne peuvent pas être inférieures à la date du calendrier d'admission,
-Expired,Expiré,
-Export,Exporter,
-Export not allowed. You need {0} role to export.,Pas autorisé à exporter. Vous devez avoir le rôle {0} pour exporter.,
 Failed to add Domain,Impossible d'ajouter le domaine,
 Fetch Items from Warehouse,Récupérer des articles de l'entrepôt,
 Fetching...,Aller chercher...,
-Field,Champ,
-Filters,Filtres,
 Finding linked payments,Trouver des paiements liés,
 Fleet Management,Gestion de flotte,
 Following fields are mandatory to create address:,Les champs suivants sont obligatoires pour créer une adresse:,
@@ -3433,28 +3136,17 @@
 Future Payments,Paiements futurs,
 GST HSN Code does not exist for one or more items,Le code HSN de la TPS n’existe pas pour un ou plusieurs articles,
 Generate E-Way Bill JSON,Générer E-Way Bill JSON,
-Get Items,Obtenir les Articles,
 Get Outstanding Documents,Obtenez des documents en suspens,
-Goal,Objectif,
 Greater Than Amount,Plus grand que le montant,
-Green,Vert,
-Group,Groupe,
 Group By Customer,Regrouper par client,
 Group By Supplier,Regrouper par fournisseur,
-Group Node,Noeud de Groupe,
 Group Warehouses cannot be used in transactions. Please change the value of {0},Les entrepôts de groupe ne peuvent pas être utilisés dans les transactions. Veuillez modifier la valeur de {0},
-Help,Aidez-moi,
-Help Article,Article d’Aide,
 "Helps you keep tracks of Contracts based on Supplier, Customer and Employee","Vous aide à garder une trace des contrats en fonction du fournisseur, client et employé",
 Helps you manage appointments with your leads,Vous aide à gérer les rendez-vous avec vos leads,
-Home,Accueil,
 IBAN is not valid,IBAN n'est pas valide,
-Import Data from CSV / Excel files.,Importer des données à partir de fichiers CSV / Excel,
-In Progress,En cours,
 Incoming call from {0},Appel entrant du {0},
 Incorrect Warehouse,Entrepôt incorrect,
 Invalid Barcode. There is no Item attached to this barcode.,Code à barres invalide. Il n'y a pas d'article attaché à ce code à barres.,
-Invalid credentials,les informations d'identification invalides,
 Issue Priority.,Priorité d'émission.,
 Issue Type.,Type de probleme.,
 "It seems that there is an issue with the server's stripe configuration. In case of failure, the amount will get refunded to your account.","Il semble qu'il y a un problème avec la configuration de Stripe sur le serveur. En cas d'erreur, le montant est remboursé sur votre compte.",
@@ -3470,13 +3162,11 @@
 Leaves Taken,Feuilles prises,
 Less Than Amount,Moins que le montant,
 Liabilities,Passifs,
-Loading...,Chargement en Cours ...,
 Loan Applications from customers and employees.,Demandes de prêt des clients et des employés.,
 Loan Processes,Processus de prêt,
 Loan Type for interest and penalty rates,Type de prêt pour les taux d'intérêt et de pénalité,
 Loans,Les prêts,
 Loans provided to customers and employees.,Prêts accordés aux clients et aux employés.,
-Location,Lieu,
 Looks like someone sent you to an incomplete URL. Please ask them to look into it.,On dirait que quelqu'un vous a envoyé vers URL incomplète. Veuillez leur demander d’analyser l’erreur.,
 Make Journal Entry,Faire une écriture de journal,
 Make Purchase Invoice,Faire la facture d'achat,
@@ -3485,12 +3175,6 @@
 Master,Maître,
 Max strength cannot be less than zero.,La force maximale ne peut pas être inférieure à zéro.,
 Maximum attempts for this quiz reached!,Nombre maximal de tentatives pour ce quiz atteint!,
-Message,Message,
-Missing Values Required,Valeurs Manquantes Requises,
-Mobile No,N° Mobile,
-Mobile Number,Numéro de Mobile,
-Month,Mois,
-Name,Nom,
 Near you,Près de toi,
 Net Profit/Loss,Résultat net,
 New Expense,Nouvelle dépense,
@@ -3509,10 +3193,8 @@
 No reviews yet,Pas encore d'avis,
 No views yet,Pas encore de vue,
 Non stock items,Articles hors stock,
-Not Allowed,Non Autorisé,
 Not allowed to create accounting dimension for {0},Non autorisé à créer une dimension comptable pour {0},
 Not permitted. Please disable the Lab Test Template,Pas permis. Veuillez désactiver le modèle de test de laboratoire,
-Note,Note,
 Notes: ,Remarques :,
 On Converting Opportunity,Sur l'opportunité de conversion,
 On Purchase Order Submission,Sur soumission de commande,
@@ -3520,13 +3202,11 @@
 On Task Completion,En fin de tâche,
 On {0} Creation,Sur {0} Creation,
 Only .csv and .xlsx files are supported currently,Seuls les fichiers .csv et .xlsx sont actuellement pris en charge.,
-Open,Ouvert,
 Open Contact,Contact ouvert,
 Open Lead,Ouvrir le Lead,
 Opening and Closing,Ouverture et fermeture,
 Operating Cost as per Work Order / BOM,Coût d'exploitation selon l'ordre de fabrication / nomenclature,
 Order Amount,Montant de la commande,
-Page {0} of {1},Page {0} sur {1},
 Paid amount cannot be less than {0},Le montant payé ne peut pas être inférieur à {0},
 Parent Company must be a group company,La société mère doit être une société du groupe,
 Passing Score value should be between 0 and 100,La note de passage doit être comprise entre 0 et 100,
@@ -3535,11 +3215,9 @@
 Pay,Payer,
 Payment Document Type,Type de document de paiement,
 Payment Name,Nom du paiement,
-Pending,En Attente,
 Performance,Performance,
 Period based On,Période basée sur,
 Perpetual inventory required for the company {0} to view this report.,Inventaire permanent requis pour que la société {0} puisse consulter ce rapport.,
-Phone,Téléphone,
 Pick List,Liste de sélection,
 Plaid authentication error,Erreur d'authentification du plaid,
 Plaid public token error,Erreur de jeton public Plaid,
@@ -3570,14 +3248,11 @@
 Please set valid GSTIN No. in Company Address for company {0},Veuillez définir un numéro GSTIN valide dans l'adresse de l'entreprise pour l'entreprise {0},
 Please set {0},Veuillez définir {0},customer
 Please setup a default bank account for company {0},Veuillez configurer un compte bancaire par défaut pour la société {0}.,
-Please specify,Veuillez spécifier,
 Please specify a {0},Veuillez spécifier un {0},lead
-Priority,Priorité,
 Priority has been changed to {0}.,La priorité a été changée en {0}.,
 Priority {0} has been repeated.,La priorité {0} a été répétée.,
 Processing XML Files,Traitement des fichiers XML,
 Profitability,Rentabilité,
-Project,Projet,
 Provide the academic year and set the starting and ending date.,Indiquez l'année universitaire et définissez la date de début et de fin.,
 Public token is missing for this bank,Un jeton public est manquant pour cette banque,
 Publish 1 Item,Publier 1 élément,
@@ -3595,30 +3270,22 @@
 Quality Inspection required for Item {0} to submit,Inspection de qualité requise pour que l'élément {0} soit envoyé,
 Quantity to Manufacture,Quantité à fabriquer,
 Quantity to Manufacture can not be zero for the operation {0},La quantité à fabriquer ne peut pas être nulle pour l'opération {0},
-Quarterly,Trimestriel,
-Queued,File d'Attente,
-Quick Entry,Écriture Rapide,
 Quiz {0} does not exist,Le questionnaire {0} n'existe pas,
 Quotation Amount,Montant du devis,
 Rate or Discount is required for the price discount.,Le prix ou la remise est requis pour la remise.,
-Reason,Raison,
 Reconcile Entries,Réconcilier les entrées,
 Reconcile this account,Réconcilier ce compte,
 Reconciled,Réconcilié,
 Recruitment,Recrutement,
-Red,Rouge,
 Release date must be in the future,La date de sortie doit être dans le futur,
 Relieving Date must be greater than or equal to Date of Joining,La date de libération doit être supérieure ou égale à la date d'adhésion,
-Rename,Renommer,
 Rename Not Allowed,Renommer non autorisé,
 Report Item,Élément de rapport,
 Report this Item,Signaler cet article,
 Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.,Quantité réservée pour la sous-traitance: quantité de matières premières pour fabriquer des articles sous-traités.,
-Reset,Réinitialiser,
 Reset Service Level Agreement,Réinitialiser l'accord de niveau de service,
 Resetting Service Level Agreement.,Réinitialisation de l'accord de niveau de service.,
 Return amount cannot be greater unclaimed amount,Le montant du retour ne peut pas être supérieur au montant non réclamé,
-Review,La revue,
 Room,Chambre,
 Room Type,Type de chambre,
 Row # ,Ligne #,
@@ -3643,47 +3310,36 @@
 Row({0}): {1} is already discounted in {2},Ligne ({0}): {1} est déjà réduit dans {2}.,
 Rows Added in {0},Lignes ajoutées dans {0},
 Rows Removed in {0},Lignes supprimées dans {0},
-Save,sauvegarder,
 Save Item,Enregistrer l'élément,
 Saved Items,Articles sauvegardés,
 Search Items ...,Rechercher des articles ...,
 Search for a payment,Rechercher un paiement,
 Search for anything ...,Rechercher n'importe quoi ...,
-Search results for,Résultats de recherche pour,
 Select Difference Account,Sélectionnez compte différentiel,
 Select a Default Priority.,Sélectionnez une priorité par défaut.,
 Select a company,Sélectionnez une entreprise,
 Select finance book for the item {0} at row {1},Sélectionnez le livre de financement pour l'élément {0} à la ligne {1}.,
 Select only one Priority as Default.,Sélectionnez une seule priorité par défaut.,
 Seller Information,Information du vendeur,
-Send,Envoyer,
 Send a message,Envoyer un message,
-Sending,Envoi,
 Sends Mails to lead or contact based on a Campaign schedule,Envoie des courriers à diriger ou à contacter en fonction d'un calendrier de campagne,
 Serial Number Created,Numéro de série créé,
 Serial Numbers Created,Numéros de série créés,
 Serial no(s) required for serialized item {0},N ° de série requis pour l'article sérialisé {0},
 Series,Séries,
-Server Error,Erreur du Serveur,
 Service Level Agreement has been changed to {0}.,L'accord de niveau de service a été remplacé par {0}.,
 Service Level Agreement was reset.,L'accord de niveau de service a été réinitialisé.,
 Service Level Agreement with Entity Type {0} and Entity {1} already exists.,L'accord de niveau de service avec le type d'entité {0} et l'entité {1} existe déjà.,
 Set Meta Tags,Définir les balises méta,
 Set {0} in company {1},Définissez {0} dans l'entreprise {1},
-Setup,Configuration,
 Shift Management,Gestion des quarts,
 Show Future Payments,Afficher les paiements futurs,
 Show Linked Delivery Notes,Afficher les bons de livraison liés,
 Show Sales Person,Afficher le vendeur,
 Show Stock Ageing Data,Afficher les données sur le vieillissement des stocks,
 Show Warehouse-wise Stock,Afficher le stock entre les magasins,
-Size,Taille,
 Something went wrong while evaluating the quiz.,Quelque chose s'est mal passé lors de l'évaluation du quiz.,
-Sr,Sr,
-Start,Démarrer,
 Start Date cannot be before the current date,La date de début ne peut pas être antérieure à la date du jour,
-Start Time,Heure de Début,
-Status,Statut,
 Status must be Cancelled or Completed,Le statut doit être annulé ou complété,
 Stock Balance Report,Rapport de solde des stocks,
 Stock Entry has been already created against this Pick List,Une entrée de stock a déjà été créée dans cette liste de choix,
@@ -3692,10 +3348,8 @@
 Stores - {0},Magasins - {0},
 Student with email {0} does not exist,Étudiant avec le courrier électronique {0} n'existe pas,
 Submit Review,Poster un commentaire,
-Submitted,Valider,
 Supplier Addresses And Contacts,Adresses et contacts des fournisseurs,
 Synchronize this account,Synchroniser ce compte,
-Tag,Étiquette,
 Target Location is required while receiving Asset {0} from an employee,L'emplacement cible est requis lors de la réception de l'élément {0} d'un employé,
 Target Location is required while transferring Asset {0},L'emplacement cible est requis lors du transfert de l'élément {0},
 Target Location or To Employee is required while receiving Asset {0},L'emplacement cible ou l'employé est requis lors de la réception de l'élément {0},
@@ -3703,7 +3357,6 @@
 Task's {0} Start Date cannot be after Project's End Date.,La date de début {0} de la tâche ne peut pas être postérieure à la date de fin du projet.,
 Tax Account not specified for Shopify Tax {0},Compte de taxe non spécifié pour Shopify Tax {0},
 Tax Total,Total de la taxe,
-Template,Modèle,
 The Campaign '{0}' already exists for the {1} '{2}',La campagne '{0}' existe déjà pour le {1} '{2}'.,
 The difference between from time and To Time must be a multiple of Appointment,La différence entre from time et To Time doit être un multiple de Appointment,
 The field Asset Account cannot be blank,Le champ Compte d'actif ne peut pas être vide,
@@ -3720,19 +3373,13 @@
 This bank transaction is already fully reconciled,Cette transaction bancaire est déjà totalement réconciliée,
 This page keeps track of items you want to buy from sellers.,Cette page répertorie les articles que vous souhaitez acheter auprès des vendeurs.,
 This page keeps track of your items in which buyers have showed some interest.,Cette page conserve une trace de vos articles pour lesquels les acheteurs ont manifesté un certain intérêt.,
-Thursday,Jeudi,
-Title,Titre,
 "To allow over billing, update ""Over Billing Allowance"" in Accounts Settings or the Item.","Pour autoriser la facturation excédentaire, mettez à jour &quot;Provision de facturation excédentaire&quot; dans les paramètres de compte ou le poste.",
 "To allow over receipt / delivery, update ""Over Receipt/Delivery Allowance"" in Stock Settings or the Item.","Pour autoriser le dépassement de réception / livraison, mettez à jour &quot;Limite de dépassement de réception / livraison&quot; dans les paramètres de stock ou le poste.",
-Total,Total,
 Total Payment Request amount cannot be greater than {0} amount,Le montant total de la demande de paiement ne peut être supérieur à {0}.,
 Total payments amount can't be greater than {},Le montant total des paiements ne peut être supérieur à {},
-Totals,Totaux,
 Transactions already retreived from the statement,Transactions déjà extraites de la déclaration,
 Transfer Material to Supplier,Transfert de matériel au fournisseur,
 Transport Receipt No and Date are mandatory for your chosen Mode of Transport,Le numéro de reçu de transport et la date sont obligatoires pour le mode de transport choisi,
-Tuesday,Mardi,
-Type,Type,
 Unable to find the time slot in the next {0} days for the operation {1}.,Impossible de trouver l'intervalle de temps dans les {0} jours suivants pour l'opération {1}.,
 Unable to update remote activity,Impossible de mettre à jour l'activité à distance,
 Unknown Caller,Appelant inconnu,
@@ -3740,13 +3387,10 @@
 Unpublish Item,Annuler la publication,
 Unreconciled,Non réconcilié,
 Unsupported GST Category for E-Way Bill JSON generation,Catégorie GST non prise en charge pour la génération e-Way Bill JSON,
-Update,Mettre à Jour,
 Update Taxes for Items,Mettre à jour les taxes pour les articles,
 "Upload a bank statement, link or reconcile a bank account","Télécharger un relevé bancaire, un lien ou un rapprochement d'un compte bancaire",
 Upload a statement,Télécharger une déclaration,
 Use a name that is different from previous project name,Utilisez un nom différent du nom du projet précédent,
-User {0} is disabled,Utilisateur {0} est désactivé,
-Users and Permissions,Utilisateurs et Autorisations,
 Valuation Rate required for Item {0} at row {1},Taux de valorisation requis pour le poste {0} à la ligne {1},
 Values Out Of Sync,Valeurs désynchronisées,
 Vehicle Type is required if Mode of Transport is Road,Le type de véhicule est requis si le mode de transport est la route,
@@ -3755,15 +3399,11 @@
 View,Vue,
 View all issues from {0},Afficher tous les problèmes de {0},
 View call log,Voir le journal des appels,
-Warehouse,Entrepôt,
 Warehouse not found against the account {0},Entrepôt introuvable sur le compte {0},
-Welcome to {0},Bienvenue sur {0},
 Why do think this Item should be removed?,Pourquoi pensez-vous que cet élément devrait être supprimé?,
 Work Order {0}: Job Card not found for the operation {1},Bon de travail {0}: carte de travail non trouvée pour l'opération {1},
 Workday {0} has been repeated.,La journée de travail {0} a été répétée.,
 XML Files Processed,Fichiers XML traités,
-Year,Année,
-Yearly,Annuel,
 You are not allowed to enroll for this course,Vous n'êtes pas autorisé à vous inscrire à ce cours,
 You are not enrolled in program {0},Vous n'êtes pas inscrit au programme {0},
 You can Feature upto 8 items.,Vous pouvez présenter jusqu'à 8 éléments.,
@@ -3776,7 +3416,6 @@
 Your Items,Vos articles,
 Your Profile,Votre profil,
 Your rating:,Votre note :,
-and,et,
 e-Way Bill already exists for this document,e-Way Bill existe déjà pour ce document,
 woocommerce - {0},woocommerce - {0},
 {0} Coupon used are {1}. Allowed quantity is exhausted,Le {0} coupon utilisé est {1}. La quantité autorisée est épuisée,
@@ -3788,7 +3427,6 @@
 {0} is not a company bank account,{0} n'est pas un compte bancaire d'entreprise,
 {0} is not a group node. Please select a group node as parent cost center,{0} n'est pas un nœud de groupe. Veuillez sélectionner un nœud de groupe comme centre de coûts parent,
 {0} is not the default supplier for any items.,{0} n'est le fournisseur par défaut d'aucun élément.,
-{0} is required,{0} est nécessaire,
 {0}: {1} must be less than {2},{0}: {1} doit être inférieur à {2},
 {} is required to generate E-Way Bill JSON,{} est requis pour générer e-Way Bill JSON,
 "Invalid lost reason {0}, please create a new lost reason","Motif perdu non valide {0}, veuillez créer un nouveau motif perdu",
@@ -3797,20 +3435,6 @@
 Total Expense This Year,Dépenses totales cette année,
 Total Income,Revenu total,
 Total Income This Year,Revenu total cette année,
-Barcode,code à barre,
-Clear,Clair,
-Comments,Commentaires,
-DocType,DocType,
-Download,Télécharger,
-Left,Parti,
-Link,Lien,
-New,Nouveau,
-Print,Impression,
-Reference Name,Nom de référence,
-Refresh,Actualiser,
-Success,Succès,
-Time,Temps,
-Value,Valeur,
 Actual,Réel,
 Add to Cart,Ajouter au Panier,
 Days Since Last Order,Jours depuis la dernière commande,
@@ -3824,10 +3448,6 @@
 To date cannot be before From date,La date de fin ne peut être antérieure à la date de début,
 Write Off,Reprise,
 {0} Created,{0} Créé,
-Email Id,Identifiant Email,
-No,Non,
-Reference Doctype,DocType de la Référence,
-Yes,Oui,
 Actual ,Réel,
 Add to cart,Ajouter au Panier,
 Budget,Budget,
@@ -3838,16 +3458,13 @@
 End date can not be less than start date,La date de Fin ne peut pas être antérieure à la Date de Début,
 For Default Supplier (Optional),Pour le fournisseur par défaut (facultatif),
 From date cannot be greater than To date,La Date Initiale ne peut pas être postérieure à la Date Finale,
-Group by,Grouper Par,
 In stock,En stock,
 Item name,Libellé de l'article,
 Minimum Qty,Quantité minimum,
 More details,Plus de détails,
 Nature of Supplies,Nature des fournitures,
-No Items found.,Aucun article trouvé.,
 No students found,Aucun étudiant trouvé,
 Not in stock,En rupture,
-Not permitted,Pas permis,
 Open Issues ,Tickets ouverts,
 Open Projects ,Projets ouverts,
 Open To Do ,ToDo ouvertes,
@@ -3872,8 +3489,6 @@
 hours,heures,
 received from,reçu de,
 to,à,
-Cards,Cartes,
-Percentage,Pourcentage,
 Failed to setup defaults for country {0}. Please contact support@erpnext.com,Échec de la configuration des paramètres par défaut pour le pays {0}. Veuillez contacter support@erpnext.com,
 Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it.,Ligne # {0}: l'article {1} n'est pas un article sérialisé / en lot. Il ne peut pas avoir de numéro de série / de lot contre lui.,
 Please set {0},Veuillez définir {0},
@@ -3911,9 +3526,6 @@
 YouTube,Youtube,
 Vimeo,Vimeo,
 Publish Date,Date de publication,
-Duration,Durée,
-Advanced Settings,Réglages avancés,
-Path,Chemin,
 Components,Composants,
 Verified By,Vérifié Par,
 Invalid naming series (. missing) for {0},Masque de numérotation non valide (. Manquante) pour {0},
@@ -4230,7 +3842,6 @@
 Is Income Tax Liability,Est une dette d'impôt sur le revenu,
 Is Income Tax Expense,Est une dépense d'impôt sur le revenu,
 Cash Flow Mapping Accounts,Comptes du Mapping des Flux de Trésorerie,
-account,Compte,
 Cash Flow Mapping Template,Modèle de Mapping des Flux de Trésorerie,
 Cash Flow Mapping Template Details,Détails du Modèle de Mapping des Flux de Trésorerie,
 POS-CLO-,POS-CLO-,
@@ -4474,7 +4085,6 @@
 Invoice Portion,Pourcentage de facturation,
 Payment Amount,Montant du paiement,
 Payment Term Name,Nom du terme de paiement,
-Due Date Based On,Date d'échéance basée sur,
 Day(s) after invoice date,Jour (s) après la date de la facture,
 Day(s) after the end of the invoice month,Jour (s) après la fin du mois de facture,
 Month(s) after the end of the invoice month,Mois (s) après la fin du mois de la facture,
@@ -4769,7 +4379,6 @@
 (including),(compris),
 ACC-SH-.YYYY.-,ACC-SH-.YYYY.-,
 Folio no.,No. de Folio,
-Address and Contacts,Adresse et Contacts,
 Contact List,Liste de contacts,
 Hidden list maintaining the list of contacts linked to Shareholder,Liste cachée maintenant la liste des contacts liés aux actionnaires,
 Specify conditions to calculate shipping amount,Spécifier les conditions pour calculer le montant de la livraison,
@@ -5157,9 +4766,6 @@
 Score,Score,
 Supplier Scorecard Scoring Standing,Classement de la Fiche d'Évaluation Fournisseur,
 Standing Name,Nom du Classement,
-Purple,Violet,
-Yellow,jaune,
-Orange,orange,
 Min Grade,Note Minimale,
 Max Grade,Note Maximale,
 Warn Purchase Orders,Avertir lors de Bons de Commande,
@@ -5194,7 +4800,6 @@
 Customer Details,Détails du client,
 Phone Number,Numéro de téléphone,
 Skype ID,ID Skype,
-Linked Documents,Documents liés,
 Appointment With,Rendez-vous avec,
 Calendar Event,Événement de calendrier,
 Appointment Booking Settings,Paramètres de réservation de rendez-vous,
@@ -5212,7 +4817,6 @@
 Success Redirect URL,URL de redirection réussie,
 "Leave blank for home.\nThis is relative to site URL, for example ""about"" will redirect to ""https://yoursitename.com/about""","Laissez vide pour la maison. Ceci est relatif à l'URL du site, par exemple &quot;about&quot; redirigera vers &quot;https://yoursitename.com/about&quot;",
 Appointment Booking Slots,Horaires de prise de rendez-vous,
-Day Of Week,Jour de la Semaine,
 From Time ,Horaire de Début,
 Campaign Email Schedule,Calendrier des e-mails de campagne,
 Send After (days),Envoyer après (jours),
@@ -5255,7 +4859,6 @@
 Follow Up,Suivre,
 Next Contact By,Contact Suivant Par,
 Next Contact Date,Date du Prochain Contact,
-Ends On,Se termine le,
 Address & Contact,Adresse &amp; Contact,
 Mobile No.,N° Mobile.,
 Lead Type,Type de Lead,
@@ -5302,8 +4905,6 @@
 Post Status,Statut du message,
 Posted,Publié,
 Share On,Partager sur,
-Twitter,Twitter,
-LinkedIn,LinkedIn,
 Twitter Post Id,Identifiant de publication Twitter,
 LinkedIn Post Id,Identifiant de publication LinkedIn,
 Tweet,Tweet,
@@ -5853,7 +5454,6 @@
 Normal Test Template,Modèle de Test Normal,
 Patient Demographics,Démographie du Patient,
 HLC-PAT-.YYYY.-,HLC-PAT-. AAAA.-,
-Middle Name (optional),Prénom (facultatif),
 Inpatient Status,Statut d'hospitalisation,
 "If ""Link Customer to Patient"" is checked in Healthcare Settings and an existing Customer is not selected then, a Customer will be created for this Patient for recording transactions in Accounts module.","Si «Lier le client au patient» est coché dans les paramètres de soins de santé et qu'un client existant n'est pas sélectionné, un client sera créé pour ce patient pour enregistrer les transactions dans le module Comptes.",
 Personal and Social History,Antécédents Personnels et Sociaux,
@@ -6180,7 +5780,6 @@
 Scheduled Date,Date Prévue,
 Actual Date,Date Réelle,
 Maintenance Schedule Item,Article de Calendrier d'Entretien,
-Random,aléatoire,
 No of Visits,Nb de Visites,
 MAT-MVS-.YYYY.-,MAT-MVS-. AAAA.-,
 Maintenance Date,Date de l'Entretien,
@@ -6430,7 +6029,6 @@
 Payment ID,ID de paiement,
 Membership Settings,Paramètres d'adhésion,
 Enable RazorPay For Memberships,Activer RazorPay pour les adhésions,
-RazorPay Settings,Paramètres de RazorPay,
 Billing Cycle,Cycle de facturation,
 Billing Frequency,Fréquence de facturation,
 "The number of billing cycles for which the customer should be charged. For example, if a customer is buying a 1-year membership that should be billed on a monthly basis, this value should be 12.","Le nombre de cycles de facturation pour lesquels le client doit être facturé. Par exemple, si un client achète un abonnement d'un an qui doit être facturé sur une base mensuelle, cette valeur doit être de 12.",
@@ -7009,7 +6607,7 @@
 Send with Attachment,Envoyer avec pièce jointe,
 Delay between Delivery Stops,Délai entre les arrêts de livraison,
 Delivery Stop,Étape de Livraison,
-Lock,Fermer à clé,
+Lock,Verrouiller,
 Visited,Visité,
 Order Information,Informations sur la commande,
 Contact Information,Informations de contact,
@@ -8191,7 +7789,6 @@
 Academic Term and Program,Terme académique et programme,
 Please remove this item and try to submit again or update the posting time.,Veuillez supprimer cet élément et réessayer de le valider ou mettre à jour l'heure de publication.,
 Failed to Authenticate the API key.,Échec de l'authentification de la clé API.,
-Invalid Credentials,Les informations d'identification invalides,
 URL can only be a string,L'URL ne peut être qu'une chaîne,
 "Here is your webhook secret, this will be shown to you only once.","Voici votre secret de webhook, il ne vous sera montré qu'une seule fois.",
 The payment for this membership is not paid. To generate invoice fill the payment details,"Le paiement de cette adhésion n'est pas payé. Pour générer une facture, remplissez les détails du paiement",
@@ -8756,7 +8353,6 @@
 Billing Address Details,Adresse de facturation (détails)
 Supplier Address Details,Adresse Fournisseur (détails)
 Retail,Commerce,
-Users,Utilisateurs,
 Permission Manager,Gestion des permissions,
 Fetch Timesheet,Récuprer les temps saisis,
 Get Supplier Group Details,Appliquer les informations depuis le Groupe de fournisseur,
diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py
index e967f70..7897c15 100644
--- a/erpnext/utilities/product.py
+++ b/erpnext/utilities/product.py
@@ -2,93 +2,12 @@
 # License: GNU General Public License v3. See license.txt
 
 import frappe
-from frappe.utils import cint, flt, fmt_money, getdate, nowdate
+from frappe.utils import cint, flt, fmt_money
 
 from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item
-from erpnext.stock.doctype.batch.batch import get_batch_qty
-from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
 
 
-def get_web_item_qty_in_stock(item_code, item_warehouse_field, warehouse=None):
-	in_stock, stock_qty = 0, ""
-	template_item_code, is_stock_item = frappe.db.get_value(
-		"Item", item_code, ["variant_of", "is_stock_item"]
-	)
-
-	if not warehouse:
-		warehouse = frappe.db.get_value("Website Item", {"item_code": item_code}, item_warehouse_field)
-
-	if not warehouse and template_item_code and template_item_code != item_code:
-		warehouse = frappe.db.get_value(
-			"Website Item", {"item_code": template_item_code}, item_warehouse_field
-		)
-
-	if warehouse and frappe.get_cached_value("Warehouse", warehouse, "is_group") == 1:
-		warehouses = get_child_warehouses(warehouse)
-	else:
-		warehouses = [warehouse] if warehouse else []
-
-	total_stock = 0.0
-	if warehouses:
-		for warehouse in warehouses:
-			stock_qty = frappe.db.sql(
-				"""
-				select GREATEST(S.actual_qty - S.reserved_qty - S.reserved_qty_for_production - S.reserved_qty_for_sub_contract, 0) / IFNULL(C.conversion_factor, 1)
-				from tabBin S
-				inner join `tabItem` I on S.item_code = I.Item_code
-				left join `tabUOM Conversion Detail` C on I.sales_uom = C.uom and C.parent = I.Item_code
-				where S.item_code=%s and S.warehouse=%s""",
-				(item_code, warehouse),
-			)
-
-			if stock_qty:
-				total_stock += adjust_qty_for_expired_items(item_code, stock_qty, warehouse)
-
-		in_stock = total_stock > 0 and 1 or 0
-
-	return frappe._dict(
-		{"in_stock": in_stock, "stock_qty": total_stock, "is_stock_item": is_stock_item}
-	)
-
-
-def adjust_qty_for_expired_items(item_code, stock_qty, warehouse):
-	batches = frappe.get_all("Batch", filters=[{"item": item_code}], fields=["expiry_date", "name"])
-	expired_batches = get_expired_batches(batches)
-	stock_qty = [list(item) for item in stock_qty]
-
-	for batch in expired_batches:
-		if warehouse:
-			stock_qty[0][0] = max(0, stock_qty[0][0] - get_batch_qty(batch, warehouse))
-		else:
-			stock_qty[0][0] = max(0, stock_qty[0][0] - qty_from_all_warehouses(get_batch_qty(batch)))
-
-		if not stock_qty[0][0]:
-			break
-
-	return stock_qty[0][0] if stock_qty else 0
-
-
-def get_expired_batches(batches):
-	"""
-	:param batches: A list of dict in the form [{'expiry_date': datetime.date(20XX, 1, 1), 'name': 'batch_id'}, ...]
-	"""
-	return [b.name for b in batches if b.expiry_date and b.expiry_date <= getdate(nowdate())]
-
-
-def qty_from_all_warehouses(batch_info):
-	"""
-	:param batch_info: A list of dict in the form [{u'warehouse': u'Stores - I', u'qty': 0.8}, ...]
-	"""
-	qty = 0
-	for batch in batch_info:
-		qty = qty + batch.qty
-
-	return qty
-
-
-def get_price(item_code, price_list, customer_group, company, qty=1):
-	from erpnext.e_commerce.shopping_cart.cart import get_party
-
+def get_price(item_code, price_list, customer_group, company, qty=1, party=None):
 	template_item_code = frappe.db.get_value("Item", item_code, "variant_of")
 
 	if price_list:
@@ -106,7 +25,6 @@
 			)
 
 		if price:
-			party = get_party()
 			pricing_rule_dict = frappe._dict(
 				{
 					"item_code": item_code,
@@ -187,16 +105,62 @@
 			return price_obj
 
 
-def get_non_stock_item_status(item_code, item_warehouse_field):
-	# if item is a product bundle, check if its bundle items are in stock
-	if frappe.db.exists("Product Bundle", item_code):
-		items = frappe.get_doc("Product Bundle", item_code).get_all_children()
-		bundle_warehouse = frappe.db.get_value(
-			"Website Item", {"item_code": item_code}, item_warehouse_field
+def get_item_codes_by_attributes(attribute_filters, template_item_code=None):
+	items = []
+
+	for attribute, values in attribute_filters.items():
+		attribute_values = values
+
+		if not isinstance(attribute_values, list):
+			attribute_values = [attribute_values]
+
+		if not attribute_values:
+			continue
+
+		wheres = []
+		query_values = []
+		for attribute_value in attribute_values:
+			wheres.append("( attribute = %s and attribute_value = %s )")
+			query_values += [attribute, attribute_value]
+
+		attribute_query = " or ".join(wheres)
+
+		if template_item_code:
+			variant_of_query = "AND t2.variant_of = %s"
+			query_values.append(template_item_code)
+		else:
+			variant_of_query = ""
+
+		query = """
+			SELECT
+				t1.parent
+			FROM
+				`tabItem Variant Attribute` t1
+			WHERE
+				1 = 1
+				AND (
+					{attribute_query}
+				)
+				AND EXISTS (
+					SELECT
+						1
+					FROM
+						`tabItem` t2
+					WHERE
+						t2.name = t1.parent
+						{variant_of_query}
+				)
+			GROUP BY
+				t1.parent
+			ORDER BY
+				NULL
+		""".format(
+			attribute_query=attribute_query, variant_of_query=variant_of_query
 		)
-		return all(
-			get_web_item_qty_in_stock(d.item_code, item_warehouse_field, bundle_warehouse).in_stock
-			for d in items
-		)
-	else:
-		return 1
+
+		item_codes = set([r[0] for r in frappe.db.sql(query, query_values)])
+		items.append(item_codes)
+
+	res = list(set.intersection(*items))
+
+	return res
diff --git a/erpnext/www/all-products/__init__.py b/erpnext/www/all-products/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/www/all-products/__init__.py
+++ /dev/null
diff --git a/erpnext/www/all-products/index.html b/erpnext/www/all-products/index.html
deleted file mode 100644
index 04fc74c..0000000
--- a/erpnext/www/all-products/index.html
+++ /dev/null
@@ -1,51 +0,0 @@
-{% from "erpnext/templates/includes/macros.html" import attribute_filter_section, field_filter_section, discount_range_filters %}
-{% extends "templates/web.html" %}
-
-{% block title %}{{ _('All Products') }}{% endblock %}
-{% block header %}
-<div class="mb-6">{{ _('All Products') }}</div>
-{% endblock header %}
-
-{% block page_content %}
-<div class="row">
-	<!-- Items section -->
-	<div id="product-listing" class="col-12 order-2 col-md-9 order-md-2 item-card-group-section">
-		<!-- Rendered via JS -->
-	</div>
-
-	<!-- Filters Section -->
-	<div class="col-12 order-1 col-md-3 order-md-1">
-		<div class="collapse d-md-block mr-4 filters-section" id="product-filters">
-			<div class="d-flex justify-content-between align-items-center mb-5 title-section">
-				<div class="mb-4 filters-title" > {{ _('Filters') }} </div>
-				<a class="mb-4 clear-filters" href="/all-products">{{ _('Clear All') }}</a>
-			</div>
-			<!-- field filters -->
-			{% if field_filters %}
-				{{ field_filter_section(field_filters) }}
-			{% endif %}
-
-			<!-- attribute filters -->
-			{% if attribute_filters %}
-				{{ attribute_filter_section(attribute_filters) }}
-			{% endif %}
-		</div>
-
-	</div>
-</div>
-
-<script>
-	frappe.ready(() => {
-		$('.btn-prev, .btn-next').click((e) => {
-			const $btn = $(e.target);
-			$btn.prop('disabled', true);
-			const start = $btn.data('start');
-			let query_params = frappe.utils.get_query_params();
-			query_params.start = start;
-			let path = window.location.pathname + '?' + frappe.utils.get_url_from_dict(query_params);
-			window.location.href = path;
-		});
-	});
-</script>
-
-{% endblock %}
diff --git a/erpnext/www/all-products/index.js b/erpnext/www/all-products/index.js
deleted file mode 100644
index 98a8441..0000000
--- a/erpnext/www/all-products/index.js
+++ /dev/null
@@ -1,27 +0,0 @@
-$(() => {
-	class ProductListing {
-		constructor() {
-			let me = this;
-			let is_item_group_page = $(".item-group-content").data("item-group");
-			this.item_group = is_item_group_page || null;
-
-			let view_type = localStorage.getItem("product_view") || "List View";
-
-			// Render Product Views, Filters & Search
-			new erpnext.ProductView({
-				view_type: view_type,
-				products_section: $('#product-listing'),
-				item_group: me.item_group
-			});
-
-			this.bind_card_actions();
-		}
-
-		bind_card_actions() {
-			erpnext.e_commerce.shopping_cart.bind_add_to_cart_action();
-			erpnext.e_commerce.wishlist.bind_wishlist_action();
-		}
-	}
-
-	new ProductListing();
-});
diff --git a/erpnext/www/all-products/index.py b/erpnext/www/all-products/index.py
deleted file mode 100644
index fbf0dce..0000000
--- a/erpnext/www/all-products/index.py
+++ /dev/null
@@ -1,22 +0,0 @@
-import frappe
-from frappe.utils import cint
-
-from erpnext.e_commerce.product_data_engine.filters import ProductFiltersBuilder
-
-sitemap = 1
-
-
-def get_context(context):
-	# Add homepage as parent
-	context.body_class = "product-page"
-	context.parents = [{"name": frappe._("Home"), "route": "/"}]
-
-	filter_engine = ProductFiltersBuilder()
-	context.field_filters = filter_engine.get_field_filters()
-	context.attribute_filters = filter_engine.get_attribute_filters()
-
-	context.page_length = (
-		cint(frappe.db.get_single_value("E Commerce Settings", "products_per_page")) or 20
-	)
-
-	context.no_cache = 1
diff --git a/erpnext/www/all-products/not_found.html b/erpnext/www/all-products/not_found.html
deleted file mode 100644
index 91989a9..0000000
--- a/erpnext/www/all-products/not_found.html
+++ /dev/null
@@ -1 +0,0 @@
-<div class="d-flex justify-content-center p-3 text-muted">{{ _('No products found') }}</div>
diff --git a/erpnext/www/shop-by-category/__init__.py b/erpnext/www/shop-by-category/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/www/shop-by-category/__init__.py
+++ /dev/null
diff --git a/erpnext/www/shop-by-category/category_card_section.html b/erpnext/www/shop-by-category/category_card_section.html
deleted file mode 100644
index 56cb63a..0000000
--- a/erpnext/www/shop-by-category/category_card_section.html
+++ /dev/null
@@ -1,30 +0,0 @@
-{%- macro card(title, image, type, url=None, text_primary=False) -%}
-<!-- style defined at shop-by-category index -->
-<div class="card category-card" data-type="{{ type }}" data-name="{{ title }}">
-	{% if image %}
-	<img class="card-img-top" src="{{ image }}" alt="{{ title }}" style="height: 80%;">
-	{% else %}
-	<div class="placeholder-div">
-		<span class="placeholder">
-			{{ frappe.utils.get_abbr(title) }}
-		</span>
-	</div>
-	{% endif %}
-	<div class="card-body text-center text-muted">
-		{{ title or '' }}
-	</div>
-	<a href="{{ url or '#' }}" class="stretched-link"></a>
-</div>
-{%- endmacro -%}
-
-<div class="col-12 item-card-group-section">
-	<div class="row products-list product-category-section">
-		{%- for row in data -%}
-			{%- set title = row.name -%}
-			{%- set image = row.get("image") -%}
-			{%- if title -%}
-				{{ card(title, image, type, row.get("route")) }}
-			{%- endif -%}
-		{%- endfor -%}
-	</div>
-</div>
\ No newline at end of file
diff --git a/erpnext/www/shop-by-category/index.html b/erpnext/www/shop-by-category/index.html
deleted file mode 100644
index 04d2d57..0000000
--- a/erpnext/www/shop-by-category/index.html
+++ /dev/null
@@ -1,48 +0,0 @@
-{% extends "templates/web.html" %}
-{% block title %}{{ _('Shop by Category') }}{% endblock %}
-
-{% block head_include %}
-<style>
-	.category-slideshow {
-		margin-bottom: 2rem;
-	}
-	.category-card {
-		height: 300px !important;
-		width: 300px !important;
-		margin: 30px !important;
-	}
-</style>
-{% endblock %}
-
-{% block script %}
-<script type="text/javascript" src="/shop-by-category/index.js"></script>
-{% endblock %}
-
-{% block page_content %}
-<div class="shop-by-category-content">
-	<div class="category-slideshow">
-		{% if slideshow %}
-		<!-- slideshow -->
-			{{ web_block(
-				"Hero Slider",
-				values=slideshow,
-				add_container=0,
-				add_top_padding=0,
-				add_bottom_padding=0,
-			) }}
-		{% endif %}
-	</div>
-	<div class="category-tabs">
-		{% if tabs %}
-		<!-- tabs -->
-			{{ web_block(
-				"Section with Tabs",
-				values=tabs,
-				add_container=0,
-				add_top_padding=0,
-				add_bottom_padding=0
-			) }}
-		{% endif %}
-	</div>
-</div>
-{% endblock %}
\ No newline at end of file
diff --git a/erpnext/www/shop-by-category/index.js b/erpnext/www/shop-by-category/index.js
deleted file mode 100644
index 1b3116f..0000000
--- a/erpnext/www/shop-by-category/index.js
+++ /dev/null
@@ -1,12 +0,0 @@
-$(() => {
-	$('.category-card').on('click', (e) => {
-		let category_type = e.currentTarget.dataset.type;
-		let category_name = e.currentTarget.dataset.name;
-
-		if (category_type != "item_group") {
-			let filters = {};
-			filters[category_type] =  [category_name];
-			window.location.href = "/all-products?field_filters=" + JSON.stringify(filters);
-		}
-	});
-});
\ No newline at end of file
diff --git a/erpnext/www/shop-by-category/index.py b/erpnext/www/shop-by-category/index.py
deleted file mode 100644
index 913c183..0000000
--- a/erpnext/www/shop-by-category/index.py
+++ /dev/null
@@ -1,91 +0,0 @@
-import frappe
-from frappe import _
-
-sitemap = 1
-
-
-def get_context(context):
-	context.body_class = "product-page"
-
-	settings = frappe.get_cached_doc("E Commerce Settings")
-	context.categories_enabled = settings.enable_field_filters
-
-	if context.categories_enabled:
-		categories = [row.fieldname for row in settings.filter_fields]
-		context.tabs = get_tabs(categories)
-
-	if settings.slideshow:
-		context.slideshow = get_slideshow(settings.slideshow)
-
-	context.no_cache = 1
-
-
-def get_slideshow(slideshow):
-	values = {"show_indicators": 1, "show_controls": 1, "rounded": 1, "slider_name": "Categories"}
-	slideshow = frappe.get_cached_doc("Website Slideshow", slideshow)
-	slides = slideshow.get({"doctype": "Website Slideshow Item"})
-	for index, slide in enumerate(slides, start=1):
-		values[f"slide_{index}_image"] = slide.image
-		values[f"slide_{index}_title"] = slide.heading
-		values[f"slide_{index}_subtitle"] = slide.description
-		values[f"slide_{index}_theme"] = slide.get("theme") or "Light"
-		values[f"slide_{index}_content_align"] = slide.get("content_align") or "Centre"
-		values[f"slide_{index}_primary_action"] = slide.url
-
-	return values
-
-
-def get_tabs(categories):
-	tab_values = {
-		"title": _("Shop by Category"),
-	}
-
-	categorical_data = get_category_records(categories)
-	for index, tab in enumerate(categorical_data, start=1):
-		tab_values[f"tab_{index + 1}_title"] = frappe.unscrub(tab)
-		# pre-render cards for each tab
-		tab_values[f"tab_{index + 1}_content"] = frappe.render_template(
-			"erpnext/www/shop-by-category/category_card_section.html",
-			{"data": categorical_data[tab], "type": tab},
-		)
-	return tab_values
-
-
-def get_category_records(categories: list):
-	categorical_data = {}
-	website_item_meta = frappe.get_meta("Website Item", cached=True)
-
-	for c in categories:
-		if c == "item_group":
-			categorical_data["item_group"] = frappe.db.get_all(
-				"Item Group",
-				filters={"parent_item_group": "All Item Groups", "show_in_website": 1},
-				fields=["name", "parent_item_group", "is_group", "image", "route"],
-			)
-
-			continue
-
-		field_type = website_item_meta.get_field(c).fieldtype
-
-		if field_type == "Table MultiSelect":
-			child_doc = website_item_meta.get_field(c).options
-			for field in frappe.get_meta(child_doc, cached=True).fields:
-				if field.fieldtype == "Link" and field.reqd:
-					doctype = field.options
-		else:
-			doctype = website_item_meta.get_field(c).options
-
-		fields = ["name"]
-
-		try:
-			meta = frappe.get_meta(doctype, cached=True)
-			if meta.get_field("image"):
-				fields += ["image"]
-
-			data = frappe.db.get_all(doctype, fields=fields)
-			categorical_data[c] = data
-		except BaseException:
-			frappe.throw(_("DocType {} not found").format(doctype))
-			continue
-
-	return categorical_data
diff --git a/pyproject.toml b/pyproject.toml
index 7841c92..604aa44 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -16,11 +16,9 @@
     "holidays~=0.28",
 
     # integration dependencies
-    "gocardless-pro~=1.22.0",
     "googlemaps",
     "plaid-python~=7.2.1",
     "python-youtube~=0.8.0",
-    "tweepy~=4.14.0",
 
     # Not used directly - required by PyQRCode for PNG generation
     "pypng~=0.20220715.0",