fix: allow to select parent warehouse in the website item (#37047)

diff --git a/erpnext/e_commerce/doctype/website_item/test_website_item.py b/erpnext/e_commerce/doctype/website_item/test_website_item.py
index 43b2f67..2ba84c0 100644
--- a/erpnext/e_commerce/doctype/website_item/test_website_item.py
+++ b/erpnext/e_commerce/doctype/website_item/test_website_item.py
@@ -312,7 +312,7 @@
 		# 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][0], 0)
+		self.assertEqual(data.product_info["stock_qty"], 0)
 
 		# disable show stock availability
 		setup_e_commerce_settings({"show_stock_availability": 0})
@@ -355,7 +355,7 @@
 		# 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"][0][0], 2)
+		self.assertEqual(data.product_info["stock_qty"], 2)
 
 		# unset warehouse
 		frappe.db.set_value("Website Item", {"item_code": item_code}, "website_warehouse", "")
@@ -364,7 +364,7 @@
 		# (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(bool(data.product_info["stock_qty"]))
+		self.assertFalse(data.product_info["stock_qty"])
 
 		# disable show stock availability
 		setup_e_commerce_settings({"show_stock_availability": 0})
diff --git a/erpnext/e_commerce/doctype/website_item/website_item.js b/erpnext/e_commerce/doctype/website_item/website_item.js
index 7b7193e..b6595cc 100644
--- a/erpnext/e_commerce/doctype/website_item/website_item.js
+++ b/erpnext/e_commerce/doctype/website_item/website_item.js
@@ -5,12 +5,6 @@
 	onload: (frm) => {
 		// should never check Private
 		frm.fields_dict["website_image"].df.is_private = 0;
-
-		frm.set_query("website_warehouse", () => {
-			return {
-				filters: {"is_group": 0}
-			};
-		});
 	},
 
 	refresh: (frm) => {
diff --git a/erpnext/e_commerce/doctype/website_item/website_item.json b/erpnext/e_commerce/doctype/website_item/website_item.json
index 6556eab..6f551a0 100644
--- a/erpnext/e_commerce/doctype/website_item/website_item.json
+++ b/erpnext/e_commerce/doctype/website_item/website_item.json
@@ -135,7 +135,7 @@
    "fieldtype": "Column Break"
   },
   {
-   "description": "Show Stock availability based on this warehouse.",
+   "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,
@@ -348,7 +348,7 @@
  "index_web_pages_for_search": 1,
  "links": [],
  "make_attachments_public": 1,
- "modified": "2022-09-30 04:01:52.090732",
+ "modified": "2023-09-12 14:19:22.822689",
  "modified_by": "Administrator",
  "module": "E-commerce",
  "name": "Website Item",
diff --git a/erpnext/e_commerce/product_data_engine/query.py b/erpnext/e_commerce/product_data_engine/query.py
index e6a595a..975f876 100644
--- a/erpnext/e_commerce/product_data_engine/query.py
+++ b/erpnext/e_commerce/product_data_engine/query.py
@@ -259,6 +259,10 @@
 			)
 
 	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")
@@ -274,11 +278,7 @@
 			else:
 				item.in_stock = True
 		elif warehouse:
-			# stock item and has warehouse
-			actual_qty = frappe.db.get_value(
-				"Bin", {"item_code": item.item_code, "warehouse": item.get("website_warehouse")}, "actual_qty"
-			)
-			item.in_stock = bool(flt(actual_qty))
+			item.in_stock = get_stock_availability_from_template(item.item_code, warehouse)
 
 	def get_cart_items(self):
 		customer = get_customer(silent=True)
diff --git a/erpnext/e_commerce/shopping_cart/cart.py b/erpnext/e_commerce/shopping_cart/cart.py
index c66ae1d..7c7e169 100644
--- a/erpnext/e_commerce/shopping_cart/cart.py
+++ b/erpnext/e_commerce/shopping_cart/cart.py
@@ -111,8 +111,8 @@
 				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[0][0]:
-					throw(_("Only {0} in Stock for item {1}").format(item_stock.stock_qty[0][0], 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()
@@ -150,6 +150,10 @@
 			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(
@@ -159,11 +163,13 @@
 					"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)
 
@@ -317,6 +323,10 @@
 				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
 
diff --git a/erpnext/e_commerce/variant_selector/utils.py b/erpnext/e_commerce/variant_selector/utils.py
index 4466c45..88356f5 100644
--- a/erpnext/e_commerce/variant_selector/utils.py
+++ b/erpnext/e_commerce/variant_selector/utils.py
@@ -104,6 +104,8 @@
 
 @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.
@@ -168,7 +170,7 @@
 		product_info = None
 
 	product_id = ""
-	website_warehouse = ""
+	warehouse = ""
 	if exact_match or filtered_items:
 		if exact_match and len(exact_match) == 1:
 			product_id = exact_match[0]
@@ -176,16 +178,19 @@
 			product_id = list(filtered_items)[0]
 
 	if product_id:
-		website_warehouse = frappe.get_cached_value(
+		warehouse = frappe.get_cached_value(
 			"Website Item", {"item_code": product_id}, "website_warehouse"
 		)
 
 	available_qty = 0.0
-	if website_warehouse:
-		available_qty = flt(
-			frappe.db.get_value(
-				"Bin", {"item_code": product_id, "warehouse": website_warehouse}, "actual_qty"
-			)
+	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 {
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 79e6488..a6ab63b 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -1292,6 +1292,9 @@
 
 @frappe.whitelist()
 def get_valuation_rate(item_code, company, warehouse=None):
+	if frappe.get_cached_value("Warehouse", warehouse, "is_group"):
+		return {"valuation_rate": 0.0}
+
 	item = get_item_defaults(item_code, company)
 	item_group = get_item_group_defaults(item_code, company)
 	brand = get_brand_defaults(item_code, company)
diff --git a/erpnext/templates/generators/item/item_add_to_cart.html b/erpnext/templates/generators/item/item_add_to_cart.html
index 1381dfe..9bd3f75 100644
--- a/erpnext/templates/generators/item/item_add_to_cart.html
+++ b/erpnext/templates/generators/item/item_add_to_cart.html
@@ -49,7 +49,7 @@
 				<span class="in-green has-stock">
 					{{ _('In stock') }}
 					{% if product_info.show_stock_qty and product_info.stock_qty %}
-						({{ product_info.stock_qty[0][0] }})
+						({{ product_info.stock_qty }})
 					{% endif %}
 				</span>
 			{% endif %}
diff --git a/erpnext/templates/pages/wishlist.py b/erpnext/templates/pages/wishlist.py
index d70f27c..17607e4 100644
--- a/erpnext/templates/pages/wishlist.py
+++ b/erpnext/templates/pages/wishlist.py
@@ -25,9 +25,19 @@
 
 
 def get_stock_availability(item_code, warehouse):
-	stock_qty = frappe.utils.flt(
-		frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "actual_qty")
-	)
+	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)
 
 
diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py
index afe9654..e967f70 100644
--- a/erpnext/utilities/product.py
+++ b/erpnext/utilities/product.py
@@ -6,6 +6,7 @@
 
 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):
@@ -22,23 +23,31 @@
 			"Website Item", {"item_code": template_item_code}, item_warehouse_field
 		)
 
-	if warehouse:
-		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 warehouse and frappe.get_cached_value("Warehouse", warehouse, "is_group") == 1:
+		warehouses = get_child_warehouses(warehouse)
+	else:
+		warehouses = [warehouse] if warehouse else []
 
-		if stock_qty:
-			stock_qty = adjust_qty_for_expired_items(item_code, stock_qty, warehouse)
-			in_stock = stock_qty[0][0] > 0 and 1 or 0
+	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": stock_qty, "is_stock_item": is_stock_item}
+		{"in_stock": in_stock, "stock_qty": total_stock, "is_stock_item": is_stock_item}
 	)
 
 
@@ -56,7 +65,7 @@
 		if not stock_qty[0][0]:
 			break
 
-	return stock_qty
+	return stock_qty[0][0] if stock_qty else 0
 
 
 def get_expired_batches(batches):