Merge pull request #39733 from s-aga-r/FIX-9458

fix: update company in serial no doc
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 777a5bb..def2838 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -13,16 +13,9 @@
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
 	get_checks_for_pl_and_bs_accounts,
 )
-from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import (
-	get_dimension_filter_map,
-)
 from erpnext.accounts.party import validate_party_frozen_disabled, validate_party_gle_currency
 from erpnext.accounts.utils import get_account_currency, get_fiscal_year
-from erpnext.exceptions import (
-	InvalidAccountCurrency,
-	InvalidAccountDimensionError,
-	MandatoryAccountDimensionError,
-)
+from erpnext.exceptions import InvalidAccountCurrency
 
 exclude_from_linked_with = True
 
@@ -98,7 +91,6 @@
 		if not self.flags.from_repost and self.voucher_type != "Period Closing Voucher":
 			self.validate_account_details(adv_adj)
 			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)
 
@@ -208,42 +200,6 @@
 						)
 					)
 
-	def validate_allowed_dimensions(self):
-		dimension_filter_map = get_dimension_filter_map()
-		for key, value in dimension_filter_map.items():
-			dimension = key[0]
-			account = key[1]
-
-			if self.account == account:
-				if value["is_mandatory"] and not self.get(dimension):
-					frappe.throw(
-						_("{0} is mandatory for account {1}").format(
-							frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)
-						),
-						MandatoryAccountDimensionError,
-					)
-
-				if value["allow_or_restrict"] == "Allow":
-					if self.get(dimension) and self.get(dimension) not in value["allowed_dimensions"]:
-						frappe.throw(
-							_("Invalid value {0} for {1} against account {2}").format(
-								frappe.bold(self.get(dimension)),
-								frappe.bold(frappe.unscrub(dimension)),
-								frappe.bold(self.account),
-							),
-							InvalidAccountDimensionError,
-						)
-				else:
-					if self.get(dimension) and self.get(dimension) in value["allowed_dimensions"]:
-						frappe.throw(
-							_("Invalid value {0} for {1} against account {2}").format(
-								frappe.bold(self.get(dimension)),
-								frappe.bold(frappe.unscrub(dimension)),
-								frappe.bold(self.account),
-							),
-							InvalidAccountDimensionError,
-						)
-
 	def check_pl_account(self):
 		if (
 			self.is_opening == "Yes"
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 7bf5324..18cf4ed 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -1169,7 +1169,9 @@
 
 
 @frappe.whitelist()
-def get_default_bank_cash_account(company, account_type=None, mode_of_payment=None, account=None):
+def get_default_bank_cash_account(
+	company, account_type=None, mode_of_payment=None, account=None, ignore_permissions=False
+):
 	from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
 
 	if mode_of_payment:
@@ -1207,7 +1209,7 @@
 		return frappe._dict(
 			{
 				"account": account,
-				"balance": get_balance_on(account),
+				"balance": get_balance_on(account, ignore_account_permission=ignore_permissions),
 				"account_currency": account_details.account_currency,
 				"account_type": account_details.account_type,
 			}
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index f23d2c9..c55c820 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -2220,6 +2220,7 @@
 	party_type=None,
 	payment_type=None,
 	reference_date=None,
+	ignore_permissions=False,
 ):
 	doc = frappe.get_doc(dt, dn)
 	over_billing_allowance = frappe.db.get_single_value("Accounts Settings", "over_billing_allowance")
@@ -2242,14 +2243,14 @@
 	)
 
 	# bank or cash
-	bank = get_bank_cash_account(doc, bank_account)
+	bank = get_bank_cash_account(doc, bank_account, ignore_permissions=ignore_permissions)
 
 	# if default bank or cash account is not set in company master and party has default company bank account, fetch it
 	if party_type in ["Customer", "Supplier"] and not bank:
 		party_bank_account = get_party_bank_account(party_type, doc.get(scrub(party_type)))
 		if party_bank_account:
 			account = frappe.db.get_value("Bank Account", party_bank_account, "account")
-			bank = get_bank_cash_account(doc, account)
+			bank = get_bank_cash_account(doc, account, ignore_permissions=ignore_permissions)
 
 	paid_amount, received_amount = set_paid_amount_and_received_amount(
 		dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc
@@ -2389,9 +2390,13 @@
 		pe.set(dimension, doc.get(dimension))
 
 
-def get_bank_cash_account(doc, bank_account):
+def get_bank_cash_account(doc, bank_account, ignore_permissions=False):
 	bank = get_default_bank_cash_account(
-		doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"), account=bank_account
+		doc.company,
+		"Bank",
+		mode_of_payment=doc.get("mode_of_payment"),
+		account=bank_account,
+		ignore_permissions=ignore_permissions,
 	)
 
 	if not bank:
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index 1c8ac2f..2e82886 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -13,9 +13,13 @@
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
 	get_accounting_dimensions,
 )
+from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import (
+	get_dimension_filter_map,
+)
 from erpnext.accounts.doctype.accounting_period.accounting_period import ClosedAccountingPeriod
 from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
 from erpnext.accounts.utils import create_payment_ledger_entry
+from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError
 
 
 def make_gl_entries(
@@ -355,6 +359,7 @@
 
 	process_debit_credit_difference(gl_map)
 
+	dimension_filter_map = get_dimension_filter_map()
 	if gl_map:
 		check_freezing_date(gl_map[0]["posting_date"], adv_adj)
 		is_opening = any(d.get("is_opening") == "Yes" for d in gl_map)
@@ -362,6 +367,7 @@
 			validate_against_pcv(is_opening, gl_map[0]["posting_date"], gl_map[0]["company"])
 
 	for entry in gl_map:
+		validate_allowed_dimensions(entry, dimension_filter_map)
 		make_entry(entry, adv_adj, update_outstanding, from_repost)
 
 
@@ -700,3 +706,39 @@
 		where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
 		(now(), frappe.session.user, voucher_type, voucher_no),
 	)
+
+
+def validate_allowed_dimensions(gl_entry, dimension_filter_map):
+	for key, value in dimension_filter_map.items():
+		dimension = key[0]
+		account = key[1]
+
+		if gl_entry.account == account:
+			if value["is_mandatory"] and not gl_entry.get(dimension):
+				frappe.throw(
+					_("{0} is mandatory for account {1}").format(
+						frappe.bold(frappe.unscrub(dimension)), frappe.bold(gl_entry.account)
+					),
+					MandatoryAccountDimensionError,
+				)
+
+			if value["allow_or_restrict"] == "Allow":
+				if gl_entry.get(dimension) and gl_entry.get(dimension) not in value["allowed_dimensions"]:
+					frappe.throw(
+						_("Invalid value {0} for {1} against account {2}").format(
+							frappe.bold(gl_entry.get(dimension)),
+							frappe.bold(frappe.unscrub(dimension)),
+							frappe.bold(gl_entry.account),
+						),
+						InvalidAccountDimensionError,
+					)
+			else:
+				if gl_entry.get(dimension) and gl_entry.get(dimension) in value["allowed_dimensions"]:
+					frappe.throw(
+						_("Invalid value {0} for {1} against account {2}").format(
+							frappe.bold(gl_entry.get(dimension)),
+							frappe.bold(frappe.unscrub(dimension)),
+							frappe.bold(gl_entry.account),
+						),
+						InvalidAccountDimensionError,
+					)
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js
index 2c4c762..5374ac1 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.js
+++ b/erpnext/accounts/report/trial_balance/trial_balance.js
@@ -78,8 +78,14 @@
 			"options": erpnext.get_presentation_currency_list()
 		},
 		{
-			"fieldname": "with_period_closing_entry",
-			"label": __("Period Closing Entry"),
+			"fieldname": "with_period_closing_entry_for_opening",
+			"label": __("With Period Closing Entry For Opening Balances"),
+			"fieldtype": "Check",
+			"default": 1
+		},
+		{
+			"fieldname": "with_period_closing_entry_for_current_period",
+			"label": __("Period Closing Entry For Current Period"),
 			"fieldtype": "Check",
 			"default": 1
 		},
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py
index 8b7f0bb..2ff0eff 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -116,7 +116,7 @@
 		max_rgt,
 		filters,
 		gl_entries_by_account,
-		ignore_closing_entries=not flt(filters.with_period_closing_entry),
+		ignore_closing_entries=not flt(filters.with_period_closing_entry_for_current_period),
 		ignore_opening_entries=True,
 	)
 
@@ -249,7 +249,7 @@
 	):
 		opening_balance = opening_balance.where(closing_balance.posting_date >= filters.year_start_date)
 
-	if not flt(filters.with_period_closing_entry):
+	if not flt(filters.with_period_closing_entry_for_opening):
 		if doctype == "Account Closing Balance":
 			opening_balance = opening_balance.where(closing_balance.is_period_closing_voucher_entry == 0)
 		else:
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 4efbb27..4d94868 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -457,6 +457,7 @@
 		self.update_ordered_qty()
 		self.update_reserved_qty_for_subcontract()
 		self.update_subcontracting_order_status()
+		self.update_blanket_order()
 		self.notify_update()
 		clear_doctype_notifications(self)
 
@@ -644,6 +645,7 @@
 				update_sco_status(sco, "Closed" if self.status == "Closed" else None)
 
 
+@frappe.request_cache
 def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor=1.0):
 	"""get last purchase rate for an item"""
 
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 5405799..a30de68 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -822,6 +822,30 @@
 		# To test if the PO does NOT have a Blanket Order
 		self.assertEqual(po_doc.items[0].blanket_order, None)
 
+	def test_blanket_order_on_po_close_and_open(self):
+		# Step - 1: Create Blanket Order
+		bo = make_blanket_order(blanket_order_type="Purchasing", quantity=10, rate=10)
+
+		# Step - 2: Create Purchase Order
+		po = create_purchase_order(
+			item_code="_Test Item", qty=5, against_blanket_order=1, against_blanket=bo.name
+		)
+
+		bo.load_from_db()
+		self.assertEqual(bo.items[0].ordered_qty, 5)
+
+		# Step - 3: Close Purchase Order
+		po.update_status("Closed")
+
+		bo.load_from_db()
+		self.assertEqual(bo.items[0].ordered_qty, 0)
+
+		# Step - 4: Re-Open Purchase Order
+		po.update_status("Re-open")
+
+		bo.load_from_db()
+		self.assertEqual(bo.items[0].ordered_qty, 5)
+
 	def test_payment_terms_are_fetched_when_creating_purchase_invoice(self):
 		from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
 			create_payment_terms_template,
@@ -1148,6 +1172,7 @@
 				"schedule_date": add_days(nowdate(), 1),
 				"include_exploded_items": args.get("include_exploded_items", 1),
 				"against_blanket_order": args.against_blanket_order,
+				"against_blanket": args.against_blanket,
 				"material_request": args.material_request,
 				"material_request_item": args.material_request_item,
 			},
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 5a24cc2..e3e8def 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -545,7 +545,6 @@
    "fieldname": "blanket_order",
    "fieldtype": "Link",
    "label": "Blanket Order",
-   "no_copy": 1,
    "options": "Blanket Order"
   },
   {
@@ -553,7 +552,6 @@
    "fieldname": "blanket_order_rate",
    "fieldtype": "Currency",
    "label": "Blanket Order Rate",
-   "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
   },
@@ -917,7 +915,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-11-24 13:24:41.298416",
+ "modified": "2024-02-05 11:23:24.859435",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order Item",
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index a0569ec..e2b0ee5 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -693,7 +693,7 @@
 					if self.get("is_subcontracted"):
 						args["is_subcontracted"] = self.is_subcontracted
 
-					ret = get_item_details(args, self, for_validate=True, overwrite_warehouse=False)
+					ret = get_item_details(args, self, for_validate=for_validate, overwrite_warehouse=False)
 
 					for fieldname, value in ret.items():
 						if item.meta.get_field(fieldname) and value is not None:
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 8c43842..dc49023 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -599,7 +599,7 @@
 		if self.doctype in ["Sales Order", "Quotation"]:
 			for item in self.items:
 				item.gross_profit = flt(
-					((item.base_rate - item.valuation_rate) * item.stock_qty), self.precision("amount", item)
+					((item.base_rate - flt(item.valuation_rate)) * item.stock_qty), self.precision("amount", item)
 				)
 
 	def set_customer_address(self):
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
index b4f7708..dec7506 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
@@ -149,6 +149,13 @@
 				self.get("items")[item_count - 1].applicable_charges += diff
 
 	def validate_applicable_charges_for_item(self):
+		if self.distribute_charges_based_on == "Distribute Manually" and len(self.taxes) > 1:
+			frappe.throw(
+				_(
+					"Please keep one Applicable Charges, when 'Distribute Charges Based On' is 'Distribute Manually'. For more charges, please create another Landed Cost Voucher."
+				)
+			)
+
 		based_on = self.distribute_charges_based_on.lower()
 
 		if based_on != "distribute manually":
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index bf6080b..a1f97c9 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -1360,16 +1360,16 @@
 	for lcv in landed_cost_vouchers:
 		landed_cost_voucher_doc = frappe.get_doc("Landed Cost Voucher", lcv.parent)
 
+		based_on_field = None
 		# Use amount field for total item cost for manually cost distributed LCVs
-		if landed_cost_voucher_doc.distribute_charges_based_on == "Distribute Manually":
-			based_on_field = "amount"
-		else:
+		if landed_cost_voucher_doc.distribute_charges_based_on != "Distribute Manually":
 			based_on_field = frappe.scrub(landed_cost_voucher_doc.distribute_charges_based_on)
 
 		total_item_cost = 0
 
-		for item in landed_cost_voucher_doc.items:
-			total_item_cost += item.get(based_on_field)
+		if based_on_field:
+			for item in landed_cost_voucher_doc.items:
+				total_item_cost += item.get(based_on_field)
 
 		for item in landed_cost_voucher_doc.items:
 			if item.receipt_document == purchase_document:
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 23dacc8..9f3435e 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -1785,6 +1785,48 @@
 
 		self.assertRaises(frappe.ValidationError, se1.cancel)
 
+	def test_auto_reorder_level(self):
+		from erpnext.stock.reorder_item import reorder_item
+
+		item_doc = make_item(
+			"Test Auto Reorder Item - 001",
+			properties={"stock_uom": "Kg", "purchase_uom": "Nos", "is_stock_item": 1},
+			uoms=[{"uom": "Nos", "conversion_factor": 5}],
+		)
+
+		if not frappe.db.exists("Item Reorder", {"parent": item_doc.name}):
+			item_doc.append(
+				"reorder_levels",
+				{
+					"warehouse_reorder_level": 0,
+					"warehouse_reorder_qty": 10,
+					"warehouse": "_Test Warehouse - _TC",
+					"material_request_type": "Purchase",
+				},
+			)
+
+		item_doc.save(ignore_permissions=True)
+
+		frappe.db.set_single_value("Stock Settings", "auto_indent", 1)
+
+		mr_list = reorder_item()
+
+		frappe.db.set_single_value("Stock Settings", "auto_indent", 0)
+		mrs = frappe.get_all(
+			"Material Request Item",
+			fields=["qty", "stock_uom", "stock_qty"],
+			filters={"item_code": item_doc.name, "uom": "Nos"},
+		)
+
+		for mri in mrs:
+			self.assertEqual(mri.stock_uom, "Kg")
+			self.assertEqual(mri.stock_qty, 10)
+			self.assertEqual(mri.qty, 2)
+
+		for mr in mr_list:
+			mr.cancel()
+			mr.delete()
+
 
 def make_serialized_item(**args):
 	args = frappe._dict(args)
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index bed5285..1cb1057 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -86,7 +86,8 @@
 
 	get_party_item_code(args, item, out)
 
-	set_valuation_rate(out, args)
+	if args.get("doctype") in ["Sales Order", "Quotation"]:
+		set_valuation_rate(out, args)
 
 	update_party_blanket_order(args, out)
 
@@ -269,7 +270,9 @@
 	if not item:
 		item = frappe.get_doc("Item", args.get("item_code"))
 
-	if item.variant_of and not item.taxes:
+	if (
+		item.variant_of and not item.taxes and frappe.db.exists("Item Tax", {"parent": item.variant_of})
+	):
 		item.update_template_tables()
 
 	item_defaults = get_item_defaults(item.name, args.company)
diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py
index 276531a..59f8b20 100644
--- a/erpnext/stock/reorder_item.py
+++ b/erpnext/stock/reorder_item.py
@@ -34,73 +34,157 @@
 		erpnext.get_default_company() or frappe.db.sql("""select name from tabCompany limit 1""")[0][0]
 	)
 
-	items_to_consider = frappe.db.sql_list(
-		"""select name from `tabItem` item
-		where is_stock_item=1 and has_variants=0
-			and disabled=0
-			and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %(today)s)
-			and (exists (select name from `tabItem Reorder` ir where ir.parent=item.name)
-				or (variant_of is not null and variant_of != ''
-				and exists (select name from `tabItem Reorder` ir where ir.parent=item.variant_of))
-			)""",
-		{"today": nowdate()},
-	)
+	items_to_consider = get_items_for_reorder()
 
 	if not items_to_consider:
 		return
 
 	item_warehouse_projected_qty = get_item_warehouse_projected_qty(items_to_consider)
 
-	def add_to_material_request(
-		item_code, warehouse, reorder_level, reorder_qty, material_request_type, warehouse_group=None
-	):
-		if warehouse not in warehouse_company:
+	def add_to_material_request(**kwargs):
+		if isinstance(kwargs, dict):
+			kwargs = frappe._dict(kwargs)
+
+		if kwargs.warehouse not in warehouse_company:
 			# a disabled warehouse
 			return
 
-		reorder_level = flt(reorder_level)
-		reorder_qty = flt(reorder_qty)
+		reorder_level = flt(kwargs.reorder_level)
+		reorder_qty = flt(kwargs.reorder_qty)
 
 		# projected_qty will be 0 if Bin does not exist
-		if warehouse_group:
-			projected_qty = flt(item_warehouse_projected_qty.get(item_code, {}).get(warehouse_group))
+		if kwargs.warehouse_group:
+			projected_qty = flt(
+				item_warehouse_projected_qty.get(kwargs.item_code, {}).get(kwargs.warehouse_group)
+			)
 		else:
-			projected_qty = flt(item_warehouse_projected_qty.get(item_code, {}).get(warehouse))
+			projected_qty = flt(
+				item_warehouse_projected_qty.get(kwargs.item_code, {}).get(kwargs.warehouse)
+			)
 
 		if (reorder_level or reorder_qty) and projected_qty <= reorder_level:
 			deficiency = reorder_level - projected_qty
 			if deficiency > reorder_qty:
 				reorder_qty = deficiency
 
-			company = warehouse_company.get(warehouse) or default_company
+			company = warehouse_company.get(kwargs.warehouse) or default_company
 
-			material_requests[material_request_type].setdefault(company, []).append(
-				{"item_code": item_code, "warehouse": warehouse, "reorder_qty": reorder_qty}
+			material_requests[kwargs.material_request_type].setdefault(company, []).append(
+				{
+					"item_code": kwargs.item_code,
+					"warehouse": kwargs.warehouse,
+					"reorder_qty": reorder_qty,
+					"item_details": kwargs.item_details,
+				}
 			)
 
-	for item_code in items_to_consider:
-		item = frappe.get_doc("Item", item_code)
+	for item_code, reorder_levels in items_to_consider.items():
+		for d in reorder_levels:
+			if d.has_variants:
+				continue
 
-		if item.variant_of and not item.get("reorder_levels"):
-			item.update_template_tables()
-
-		if item.get("reorder_levels"):
-			for d in item.get("reorder_levels"):
-				add_to_material_request(
-					item_code,
-					d.warehouse,
-					d.warehouse_reorder_level,
-					d.warehouse_reorder_qty,
-					d.material_request_type,
-					warehouse_group=d.warehouse_group,
-				)
+			add_to_material_request(
+				item_code=item_code,
+				warehouse=d.warehouse,
+				reorder_level=d.warehouse_reorder_level,
+				reorder_qty=d.warehouse_reorder_qty,
+				material_request_type=d.material_request_type,
+				warehouse_group=d.warehouse_group,
+				item_details=frappe._dict(
+					{
+						"item_code": item_code,
+						"name": item_code,
+						"item_name": d.item_name,
+						"item_group": d.item_group,
+						"brand": d.brand,
+						"description": d.description,
+						"stock_uom": d.stock_uom,
+						"purchase_uom": d.purchase_uom,
+					}
+				),
+			)
 
 	if material_requests:
 		return create_material_request(material_requests)
 
 
+def get_items_for_reorder() -> dict[str, list]:
+	reorder_table = frappe.qb.DocType("Item Reorder")
+	item_table = frappe.qb.DocType("Item")
+
+	query = (
+		frappe.qb.from_(reorder_table)
+		.inner_join(item_table)
+		.on(reorder_table.parent == item_table.name)
+		.select(
+			reorder_table.warehouse,
+			reorder_table.warehouse_group,
+			reorder_table.material_request_type,
+			reorder_table.warehouse_reorder_level,
+			reorder_table.warehouse_reorder_qty,
+			item_table.name,
+			item_table.stock_uom,
+			item_table.purchase_uom,
+			item_table.description,
+			item_table.item_name,
+			item_table.item_group,
+			item_table.brand,
+			item_table.variant_of,
+			item_table.has_variants,
+		)
+		.where(
+			(item_table.disabled == 0)
+			& (item_table.is_stock_item == 1)
+			& (
+				(item_table.end_of_life.isnull())
+				| (item_table.end_of_life > nowdate())
+				| (item_table.end_of_life == "0000-00-00")
+			)
+		)
+	)
+
+	data = query.run(as_dict=True)
+	itemwise_reorder = frappe._dict({})
+	for d in data:
+		itemwise_reorder.setdefault(d.name, []).append(d)
+
+	itemwise_reorder = get_reorder_levels_for_variants(itemwise_reorder)
+
+	return itemwise_reorder
+
+
+def get_reorder_levels_for_variants(itemwise_reorder):
+	item_table = frappe.qb.DocType("Item")
+
+	query = (
+		frappe.qb.from_(item_table)
+		.select(
+			item_table.name,
+			item_table.variant_of,
+		)
+		.where(
+			(item_table.disabled == 0)
+			& (item_table.is_stock_item == 1)
+			& (
+				(item_table.end_of_life.isnull())
+				| (item_table.end_of_life > nowdate())
+				| (item_table.end_of_life == "0000-00-00")
+			)
+			& (item_table.variant_of.notnull())
+		)
+	)
+
+	variants_item = query.run(as_dict=True)
+	for row in variants_item:
+		if not itemwise_reorder.get(row.name) and itemwise_reorder.get(row.variant_of):
+			itemwise_reorder.setdefault(row.name, []).extend(itemwise_reorder.get(row.variant_of, []))
+
+	return itemwise_reorder
+
+
 def get_item_warehouse_projected_qty(items_to_consider):
 	item_warehouse_projected_qty = {}
+	items_to_consider = list(items_to_consider.keys())
 
 	for item_code, warehouse, projected_qty in frappe.db.sql(
 		"""select item_code, warehouse, projected_qty
@@ -164,7 +248,7 @@
 
 				for d in items:
 					d = frappe._dict(d)
-					item = frappe.get_doc("Item", d.item_code)
+					item = d.get("item_details")
 					uom = item.stock_uom
 					conversion_factor = 1.0
 
@@ -190,6 +274,7 @@
 							"item_code": d.item_code,
 							"schedule_date": add_days(nowdate(), cint(item.lead_time_days)),
 							"qty": qty,
+							"conversion_factor": conversion_factor,
 							"uom": uom,
 							"stock_uom": item.stock_uom,
 							"warehouse": d.warehouse,
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index ed84a5c..2693238 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -90,8 +90,7 @@
 				self.opening_data.setdefault(group_by_key, entry)
 
 	def prepare_new_data(self):
-		if not self.sle_entries:
-			return
+		self.item_warehouse_map = self.get_item_warehouse_map()
 
 		if self.filters.get("show_stock_ageing_data"):
 			self.filters["show_warehouse_wise_stock"] = True
@@ -99,7 +98,8 @@
 
 		_func = itemgetter(1)
 
-		self.item_warehouse_map = self.get_item_warehouse_map()
+		del self.sle_entries
+
 		sre_details = self.get_sre_reserved_qty_details()
 
 		variant_values = {}
@@ -143,15 +143,22 @@
 		item_warehouse_map = {}
 		self.opening_vouchers = self.get_opening_vouchers()
 
-		for entry in self.sle_entries:
-			group_by_key = self.get_group_by_key(entry)
-			if group_by_key not in item_warehouse_map:
-				self.initialize_data(item_warehouse_map, group_by_key, entry)
+		if self.filters.get("show_stock_ageing_data"):
+			self.sle_entries = self.sle_query.run(as_dict=True)
 
-			self.prepare_item_warehouse_map(item_warehouse_map, entry, group_by_key)
+		with frappe.db.unbuffered_cursor():
+			if not self.filters.get("show_stock_ageing_data"):
+				self.sle_entries = self.sle_query.run(as_dict=True, as_iterator=True)
 
-			if self.opening_data.get(group_by_key):
-				del self.opening_data[group_by_key]
+			for entry in self.sle_entries:
+				group_by_key = self.get_group_by_key(entry)
+				if group_by_key not in item_warehouse_map:
+					self.initialize_data(item_warehouse_map, group_by_key, entry)
+
+				self.prepare_item_warehouse_map(item_warehouse_map, entry, group_by_key)
+
+				if self.opening_data.get(group_by_key):
+					del self.opening_data[group_by_key]
 
 		for group_by_key, entry in self.opening_data.items():
 			if group_by_key not in item_warehouse_map:
@@ -252,7 +259,8 @@
 			.where(
 				(table.docstatus == 1)
 				& (table.company == self.filters.company)
-				& ((table.to_date <= self.from_date))
+				& (table.to_date <= self.from_date)
+				& (table.status == "Completed")
 			)
 			.orderby(table.to_date, order=Order.desc)
 			.limit(1)
@@ -305,7 +313,7 @@
 		if self.filters.get("company"):
 			query = query.where(sle.company == self.filters.get("company"))
 
-		self.sle_entries = query.run(as_dict=True)
+		self.sle_query = query
 
 	def apply_inventory_dimensions_filters(self, query, sle) -> str:
 		inventory_dimension_fields = self.get_inventory_dimension_fields()