Merge pull request #40263 from nabinhait/pi-optimization

perf: Performance optmization for Purchase Invoice submission
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index 5258214..41af06f 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -261,14 +261,16 @@
 
 
 def get_checks_for_pl_and_bs_accounts():
-	dimensions = frappe.db.sql(
-		"""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
-		FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c
-		WHERE p.name = c.parent""",
-		as_dict=1,
-	)
+	if frappe.flags.accounting_dimensions_details is None:
+		# nosemgrep
+		frappe.flags.accounting_dimensions_details = frappe.db.sql(
+			"""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
+			FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c
+			WHERE p.name = c.parent""",
+			as_dict=1,
+		)
 
-	return dimensions
+	return frappe.flags.accounting_dimensions_details
 
 
 def get_dimension_with_children(doctype, dimensions):
diff --git a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
index cb7f5f5..10dbe3b 100644
--- a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
@@ -78,6 +78,8 @@
 
 	def tearDown(self):
 		disable_dimension()
+		frappe.flags.accounting_dimensions_details = None
+		frappe.flags.dimension_filter_map = None
 
 
 def create_dimension():
diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py
index 01f6e60..2179a4d 100644
--- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py
+++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py
@@ -66,37 +66,39 @@
 
 
 def get_dimension_filter_map():
-	filters = frappe.db.sql(
-		"""
-		SELECT
-			a.applicable_on_account, d.dimension_value, p.accounting_dimension,
-			p.allow_or_restrict, a.is_mandatory
-		FROM
-			`tabApplicable On Account` a,
-			`tabAccounting Dimension Filter` p
-		LEFT JOIN `tabAllowed Dimension` d ON d.parent = p.name
-		WHERE
-			p.name = a.parent
-			AND p.disabled = 0
-	""",
-		as_dict=1,
-	)
-
-	dimension_filter_map = {}
-
-	for f in filters:
-		f.fieldname = scrub(f.accounting_dimension)
-
-		build_map(
-			dimension_filter_map,
-			f.fieldname,
-			f.applicable_on_account,
-			f.dimension_value,
-			f.allow_or_restrict,
-			f.is_mandatory,
+	if not frappe.flags.get("dimension_filter_map"):
+		filters = frappe.db.sql(
+			"""
+			SELECT
+				a.applicable_on_account, d.dimension_value, p.accounting_dimension,
+				p.allow_or_restrict, a.is_mandatory
+			FROM
+				`tabApplicable On Account` a,
+				`tabAccounting Dimension Filter` p
+			LEFT JOIN `tabAllowed Dimension` d ON d.parent = p.name
+			WHERE
+				p.name = a.parent
+				AND p.disabled = 0
+		""",
+			as_dict=1,
 		)
 
-	return dimension_filter_map
+		dimension_filter_map = {}
+
+		for f in filters:
+			f.fieldname = scrub(f.accounting_dimension)
+
+			build_map(
+				dimension_filter_map,
+				f.fieldname,
+				f.applicable_on_account,
+				f.dimension_value,
+				f.allow_or_restrict,
+				f.is_mandatory,
+			)
+		frappe.flags.dimension_filter_map = dimension_filter_map
+
+	return frappe.flags.dimension_filter_map
 
 
 def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory):
diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py
index 6aba2ab..3a7bf80 100644
--- a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py
+++ b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py
@@ -47,6 +47,8 @@
 	def tearDown(self):
 		disable_dimension_filter()
 		disable_dimension()
+		frappe.flags.accounting_dimensions_details = None
+		frappe.flags.dimension_filter_map = None
 
 		for si in self.invoice_list:
 			si.load_from_db()
diff --git a/erpnext/accounts/doctype/budget/budget.py b/erpnext/accounts/doctype/budget/budget.py
index 2cf9d97..aa77af6 100644
--- a/erpnext/accounts/doctype/budget/budget.py
+++ b/erpnext/accounts/doctype/budget/budget.py
@@ -139,6 +139,8 @@
 
 def validate_expense_against_budget(args, expense_amount=0):
 	args = frappe._dict(args)
+	if not frappe.get_all("Budget", limit=1):
+		return
 
 	if args.get("company") and not args.fiscal_year:
 		args.fiscal_year = get_fiscal_year(args.get("posting_date"), company=args.get("company"))[0]
@@ -146,6 +148,11 @@
 			"Company", args.get("company"), "exception_budget_approver_role"
 		)
 
+	if not frappe.get_cached_value(
+		"Budget", {"fiscal_year": args.fiscal_year, "company": args.company}
+	):  # nosec
+		return
+
 	if not args.account:
 		args.account = args.get("expense_account")
 
@@ -172,13 +179,13 @@
 		if (
 			args.get(budget_against)
 			and args.account
-			and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})
+			and (frappe.get_cached_value("Account", args.account, "root_type") == "Expense")
 		):
 
 			doctype = dimension.get("document_type")
 
 			if frappe.get_cached_value("DocType", doctype, "is_tree"):
-				lft, rgt = frappe.db.get_value(doctype, args.get(budget_against), ["lft", "rgt"])
+				lft, rgt = frappe.get_cached_value(doctype, args.get(budget_against), ["lft", "rgt"])
 				condition = """and exists(select name from `tab%s`
 					where lft<=%s and rgt>=%s and name=b.%s)""" % (
 					doctype,
diff --git a/erpnext/accounts/doctype/budget_account/budget_account.json b/erpnext/accounts/doctype/budget_account/budget_account.json
index ead0761..c7d8726 100644
--- a/erpnext/accounts/doctype/budget_account/budget_account.json
+++ b/erpnext/accounts/doctype/budget_account/budget_account.json
@@ -1,94 +1,42 @@
 {
- "allow_copy": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2016-05-16 11:54:09.286135", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "actions": [],
+ "creation": "2016-05-16 11:54:09.286135",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "account",
+  "budget_amount"
+ ],
  "fields": [
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "account", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Account", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Account", 
-   "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
-  }, 
+   "fieldname": "account",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Account",
+   "options": "Account",
+   "reqd": 1,
+   "search_index": 1
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "budget_amount", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Budget Amount", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Company:company:default_currency", 
-   "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
+   "fieldname": "budget_amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Budget Amount",
+   "options": "Company:company:default_currency",
+   "reqd": 1
   }
- ], 
- "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": "2017-01-02 17:02:53.339420", 
- "modified_by": "Administrator", 
- "module": "Accounts", 
- "name": "Budget Account", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_seen": 0
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2024-03-04 15:43:27.016947",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Budget Account",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index def2838..3fa8710 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -323,7 +323,7 @@
 		party_condition = ""
 
 	if against_voucher_type == "Sales Invoice":
-		party_account = frappe.db.get_value(against_voucher_type, against_voucher, "debit_to")
+		party_account = frappe.get_cached_value(against_voucher_type, against_voucher, "debit_to")
 		account_condition = "and account in ({0}, {1})".format(
 			frappe.db.escape(account), frappe.db.escape(party_account)
 		)
@@ -391,8 +391,8 @@
 def validate_frozen_account(account, adv_adj=None):
 	frozen_account = frappe.get_cached_value("Account", account, "freeze_account")
 	if frozen_account == "Yes" and not adv_adj:
-		frozen_accounts_modifier = frappe.db.get_single_value(
-			"Accounts Settings", "frozen_accounts_modifier"
+		frozen_accounts_modifier = frappe.get_cached_value(
+			"Accounts Settings", None, "frozen_accounts_modifier"
 		)
 
 		if not frozen_accounts_modifier:
diff --git a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py
index e8dfda2..3fea325 100644
--- a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py
+++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py
@@ -161,11 +161,12 @@
 	def on_update(self):
 		adv_adj = self.flags.adv_adj
 		if not self.flags.from_repost:
-			self.validate_account_details()
-			self.validate_dimensions_for_pl_and_bs()
-			self.validate_allowed_dimensions()
-			validate_balance_type(self.account, adv_adj)
 			validate_frozen_account(self.account, adv_adj)
+			if not self.delinked:
+				self.validate_account_details()
+				self.validate_dimensions_for_pl_and_bs()
+				self.validate_allowed_dimensions()
+				validate_balance_type(self.account, adv_adj)
 
 		# update outstanding amount
 		if (
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 28d4a5e..3200fc5 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -3,7 +3,7 @@
 
 
 import frappe
-from frappe import _, throw
+from frappe import _, qb, throw
 from frappe.model.mapper import get_mapped_doc
 from frappe.query_builder.functions import Sum
 from frappe.utils import cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate
@@ -742,13 +742,12 @@
 				self.db_set("repost_required", self.needs_repost)
 
 	def make_gl_entries(self, gl_entries=None, from_repost=False):
-		if not gl_entries:
-			gl_entries = self.get_gl_entries()
+		update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
+		if self.docstatus == 1:
+			if not gl_entries:
+				gl_entries = self.get_gl_entries()
 
-		if gl_entries:
-			update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
-
-			if self.docstatus == 1:
+			if gl_entries:
 				make_gl_entries(
 					gl_entries,
 					update_outstanding=update_outstanding,
@@ -756,29 +755,43 @@
 					from_repost=from_repost,
 				)
 				self.make_exchange_gain_loss_journal()
-			elif self.docstatus == 2:
-				provisional_entries = [a for a in gl_entries if a.voucher_type == "Purchase Receipt"]
-				make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
-				if provisional_entries:
-					for entry in provisional_entries:
-						frappe.db.set_value(
-							"GL Entry",
-							{"voucher_type": "Purchase Receipt", "voucher_detail_no": entry.voucher_detail_no},
-							"is_cancelled",
-							1,
-						)
-
-			if update_outstanding == "No":
-				update_outstanding_amt(
-					self.credit_to,
-					"Supplier",
-					self.supplier,
-					self.doctype,
-					self.return_against if cint(self.is_return) and self.return_against else self.name,
-				)
-
-		elif self.docstatus == 2 and cint(self.update_stock) and self.auto_accounting_for_stock:
+		elif self.docstatus == 2:
 			make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
+			self.cancel_provisional_entries()
+
+		self.update_supplier_outstanding(update_outstanding)
+
+	def cancel_provisional_entries(self):
+		rows = set()
+		purchase_receipts = set()
+		for d in self.items:
+			if d.purchase_receipt:
+				purchase_receipts.add(d.purchase_receipt)
+				rows.add(d.name)
+
+		if rows:
+			# cancel gl entries
+			gle = qb.DocType("GL Entry")
+			gle_update_query = (
+				qb.update(gle)
+				.set(gle.is_cancelled, 1)
+				.where(
+					(gle.voucher_type == "Purchase Receipt")
+					& (gle.voucher_no.isin(purchase_receipts))
+					& (gle.voucher_detail_no.isin(rows))
+				)
+			)
+			gle_update_query.run()
+
+	def update_supplier_outstanding(self, update_outstanding):
+		if update_outstanding == "No":
+			update_outstanding_amt(
+				self.credit_to,
+				"Supplier",
+				self.supplier,
+				self.doctype,
+				self.return_against if cint(self.is_return) and self.return_against else self.name,
+			)
 
 	def get_gl_entries(self, warehouse_account=None):
 		self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
@@ -891,8 +904,9 @@
 				"Company", self.company, "enable_provisional_accounting_for_non_stock_items"
 			)
 		)
-
-		purchase_receipt_doc_map = {}
+		self.provisional_enpenses_booked_in_pr = False
+		if provisional_accounting_for_non_stock_items:
+			self.get_provisional_accounts()
 
 		for item in self.get("items"):
 			if flt(item.base_net_amount):
@@ -1029,44 +1043,7 @@
 					dummy, amount = self.get_amount_and_base_amount(item, None)
 
 					if provisional_accounting_for_non_stock_items:
-						if item.purchase_receipt:
-							provisional_account, pr_qty, pr_base_rate = frappe.get_cached_value(
-								"Purchase Receipt Item",
-								item.pr_detail,
-								["provisional_expense_account", "qty", "base_rate"],
-							)
-							provisional_account = provisional_account or self.get_company_default(
-								"default_provisional_account"
-							)
-							purchase_receipt_doc = purchase_receipt_doc_map.get(item.purchase_receipt)
-
-							if not purchase_receipt_doc:
-								purchase_receipt_doc = frappe.get_doc("Purchase Receipt", item.purchase_receipt)
-								purchase_receipt_doc_map[item.purchase_receipt] = purchase_receipt_doc
-
-							# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
-							expense_booked_in_pr = frappe.db.get_value(
-								"GL Entry",
-								{
-									"is_cancelled": 0,
-									"voucher_type": "Purchase Receipt",
-									"voucher_no": item.purchase_receipt,
-									"voucher_detail_no": item.pr_detail,
-									"account": provisional_account,
-								},
-								"name",
-							)
-
-							if expense_booked_in_pr:
-								# Intentionally passing purchase invoice item to handle partial billing
-								purchase_receipt_doc.add_provisional_gl_entry(
-									item,
-									gl_entries,
-									self.posting_date,
-									provisional_account,
-									reverse=1,
-									item_amount=(min(item.qty, pr_qty) * pr_base_rate),
-								)
+						self.make_provisional_gl_entry(gl_entries, item)
 
 					if not self.is_internal_transfer():
 						gl_entries.append(
@@ -1163,6 +1140,58 @@
 			if item.is_fixed_asset and item.landed_cost_voucher_amount:
 				self.update_gross_purchase_amount_for_linked_assets(item)
 
+	def get_provisional_accounts(self):
+		self.provisional_accounts = frappe._dict()
+		linked_purchase_receipts = set([d.purchase_receipt for d in self.items if d.purchase_receipt])
+		pr_items = frappe.get_all(
+			"Purchase Receipt Item",
+			filters={"parent": ("in", linked_purchase_receipts)},
+			fields=["name", "provisional_expense_account", "qty", "base_rate"],
+		)
+		default_provisional_account = self.get_company_default("default_provisional_account")
+		for item in pr_items:
+			self.provisional_accounts[item.name] = {
+				"provisional_account": item.provisional_expense_account or default_provisional_account,
+				"qty": item.qty,
+				"base_rate": item.base_rate,
+			}
+
+	def make_provisional_gl_entry(self, gl_entries, item):
+		if item.purchase_receipt:
+			if not self.provisional_enpenses_booked_in_pr:
+				pr_item = self.provisional_accounts.get(item.pr_detail, {})
+				provisional_account = pr_item.get("provisional_account")
+				pr_qty = pr_item.get("qty")
+				pr_base_rate = pr_item.get("base_rate")
+
+				# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
+				provision_gle_against_pr = frappe.db.get_value(
+					"GL Entry",
+					{
+						"is_cancelled": 0,
+						"voucher_type": "Purchase Receipt",
+						"voucher_no": item.purchase_receipt,
+						"voucher_detail_no": item.pr_detail,
+						"account": provisional_account,
+					},
+					["name"],
+				)
+				if provision_gle_against_pr:
+					self.provisional_enpenses_booked_in_pr = True
+
+			if self.provisional_enpenses_booked_in_pr:
+				purchase_receipt_doc = frappe.get_cached_doc("Purchase Receipt", item.purchase_receipt)
+
+				# Intentionally passing purchase invoice item to handle partial billing
+				purchase_receipt_doc.add_provisional_gl_entry(
+					item,
+					gl_entries,
+					self.posting_date,
+					provisional_account,
+					reverse=1,
+					item_amount=(min(item.qty, pr_qty) * pr_base_rate),
+				)
+
 	def update_gross_purchase_amount_for_linked_assets(self, item):
 		assets = frappe.db.get_all(
 			"Asset",
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index 2e82886..825a01e 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -7,7 +7,7 @@
 import frappe
 from frappe import _
 from frappe.model.meta import get_field_precision
-from frappe.utils import cint, cstr, flt, formatdate, getdate, now
+from frappe.utils import cint, flt, formatdate, getdate, now
 
 import erpnext
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
@@ -234,11 +234,13 @@
 def merge_similar_entries(gl_map, precision=None):
 	merged_gl_map = []
 	accounting_dimensions = get_accounting_dimensions()
+	merge_properties = get_merge_properties(accounting_dimensions)
 
 	for entry in gl_map:
+		entry.merge_key = get_merge_key(entry, merge_properties)
 		# if there is already an entry in this account then just add it
 		# to that entry
-		same_head = check_if_in_list(entry, merged_gl_map, accounting_dimensions)
+		same_head = check_if_in_list(entry, merged_gl_map)
 		if same_head:
 			same_head.debit = flt(same_head.debit) + flt(entry.debit)
 			same_head.debit_in_account_currency = flt(same_head.debit_in_account_currency) + flt(
@@ -273,34 +275,35 @@
 	return merged_gl_map
 
 
-def check_if_in_list(gle, gl_map, dimensions=None):
-	account_head_fieldnames = [
-		"voucher_detail_no",
-		"party",
-		"against_voucher",
+def get_merge_properties(dimensions=None):
+	merge_properties = [
+		"account",
 		"cost_center",
-		"against_voucher_type",
+		"party",
 		"party_type",
+		"voucher_detail_no",
+		"against_voucher",
+		"against_voucher_type",
 		"project",
 		"finance_book",
 		"voucher_no",
 	]
-
 	if dimensions:
-		account_head_fieldnames = account_head_fieldnames + dimensions
+		merge_properties.extend(dimensions)
+	return merge_properties
 
+
+def get_merge_key(entry, merge_properties):
+	merge_key = []
+	for fieldname in merge_properties:
+		merge_key.append(entry.get(fieldname, ""))
+
+	return tuple(merge_key)
+
+
+def check_if_in_list(gle, gl_map):
 	for e in gl_map:
-		same_head = True
-		if e.account != gle.account:
-			same_head = False
-			continue
-
-		for fieldname in account_head_fieldnames:
-			if cstr(e.get(fieldname)) != cstr(gle.get(fieldname)):
-				same_head = False
-				break
-
-		if same_head:
+		if e.merge_key == gle.merge_key:
 			return e
 
 
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index b7e687d..8839773 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1712,8 +1712,8 @@
 		item_allowance = {}
 		global_qty_allowance, global_amount_allowance = None, None
 
-		role_allowed_to_over_bill = frappe.db.get_single_value(
-			"Accounts Settings", "role_allowed_to_over_bill"
+		role_allowed_to_over_bill = frappe.get_cached_value(
+			"Accounts Settings", None, "role_allowed_to_over_bill"
 		)
 		user_roles = frappe.get_roles()
 
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index e5f341f..fcbec22 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -577,6 +577,7 @@
 			ref_doc.set_status(update=True)
 
 
+@frappe.request_cache
 def get_allowance_for(
 	item_code,
 	item_allowance=None,
@@ -606,20 +607,20 @@
 				global_amount_allowance,
 			)
 
-	qty_allowance, over_billing_allowance = frappe.db.get_value(
+	qty_allowance, over_billing_allowance = frappe.get_cached_value(
 		"Item", item_code, ["over_delivery_receipt_allowance", "over_billing_allowance"]
 	)
 
 	if qty_or_amount == "qty" and not qty_allowance:
 		if global_qty_allowance == None:
 			global_qty_allowance = flt(
-				frappe.db.get_single_value("Stock Settings", "over_delivery_receipt_allowance")
+				frappe.get_cached_value("Stock Settings", None, "over_delivery_receipt_allowance")
 			)
 		qty_allowance = global_qty_allowance
 	elif qty_or_amount == "amount" and not over_billing_allowance:
 		if global_amount_allowance == None:
 			global_amount_allowance = flt(
-				frappe.db.get_single_value("Accounts Settings", "over_billing_allowance")
+				frappe.get_cached_value("Accounts Settings", None, "over_billing_allowance")
 			)
 		over_billing_allowance = global_amount_allowance
 
diff --git a/erpnext/stock/doctype/item_price/item_price.json b/erpnext/stock/doctype/item_price/item_price.json
index 707f346..bf944a4 100644
--- a/erpnext/stock/doctype/item_price/item_price.json
+++ b/erpnext/stock/doctype/item_price/item_price.json
@@ -104,7 +104,8 @@
    "in_standard_filter": 1,
    "label": "Price List",
    "options": "Price List",
-   "reqd": 1
+   "reqd": 1,
+   "search_index": 1
   },
   {
    "bold": 1,
@@ -220,7 +221,7 @@
  "idx": 1,
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2024-01-30 14:02:19.304854",
+ "modified": "2024-03-13 12:23:39.630290",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Item Price",
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 1cb1057..3c8b808 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -103,22 +103,8 @@
 	if args.customer and cint(args.is_pos):
 		out.update(get_pos_profile_item_details(args.company, args, update_data=True))
 
-	if (
-		args.get("doctype") == "Material Request"
-		and args.get("material_request_type") == "Material Transfer"
-	):
-		out.update(get_bin_details(args.item_code, args.get("from_warehouse")))
-
-	elif out.get("warehouse"):
-		if doc and doc.get("doctype") == "Purchase Order":
-			# calculate company_total_stock only for po
-			bin_details = get_bin_details(
-				args.item_code, out.warehouse, args.company, include_child_warehouses=True
-			)
-		else:
-			bin_details = get_bin_details(args.item_code, out.warehouse, include_child_warehouses=True)
-
-		out.update(bin_details)
+	if item.is_stock_item:
+		update_bin_details(args, out, doc)
 
 	# update args with out, if key or value not exists
 	for key, value in out.items():
@@ -169,6 +155,24 @@
 		out.update(get_valuation_rate(args.item_code, args.company, out.get("warehouse")))
 
 
+def update_bin_details(args, out, doc):
+	if (
+		args.get("doctype") == "Material Request"
+		and args.get("material_request_type") == "Material Transfer"
+	):
+		out.update(get_bin_details(args.item_code, args.get("from_warehouse")))
+
+	elif out.get("warehouse"):
+		company = args.company if (doc and doc.get("doctype") == "Purchase Order") else None
+
+		# calculate company_total_stock only for po
+		bin_details = get_bin_details(
+			args.item_code, out.warehouse, company, include_child_warehouses=True
+		)
+
+		out.update(bin_details)
+
+
 def process_args(args):
 	if isinstance(args, str):
 		args = json.loads(args)