test: test cases for inventory dimension
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index da7f5e2..40bc1aa 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -367,14 +367,13 @@
 		)
 
 		sl_dict.update(args)
-		if self.docstatus == 1:
-			self.update_inventory_dimensions(d, sl_dict)
+		self.update_inventory_dimensions(d, sl_dict)
 
 		return sl_dict
 
 	def update_inventory_dimensions(self, row, sl_dict) -> None:
 		dimension = get_evaluated_inventory_dimension(row, sl_dict, parent_doc=self)
-		if dimension:
+		if dimension and row.get(dimension.source_fieldname):
 			sl_dict[dimension.target_fieldname] = row.get(dimension.source_fieldname)
 
 	def make_sl_entries(self, sl_entries, allow_negative_stock=False, via_landed_cost_voucher=False):
diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js
index 1256659..91a21f4 100644
--- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js
+++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js
@@ -32,25 +32,15 @@
 		frm.trigger('render_traget_field');
 	},
 
-	map_with_existing_field(frm) {
-		frm.trigger('render_traget_field');
-	},
+	refresh(frm) {
+		if (frm.doc.__onload && frm.doc.__onload.has_stock_ledger
+			&& frm.doc.__onload.has_stock_ledger.length) {
+			let msg = __('Stock transactions exists against this dimension, user can not update document.');
+			frm.dashboard.add_comment(msg, 'blue', true);
 
-	render_traget_field(frm) {
-		if (frm.doc.map_with_existing_field && !frm.doc.disabled) {
-			frappe.call({
-				method: 'erpnext.stock.doctype.inventory_dimension.inventory_dimension.get_source_fieldnames',
-				args: {
-					reference_document: frm.doc.reference_document,
-					ignore_document: frm.doc.name
-				},
-				callback: function(r) {
-					if (r.message && r.message.length) {
-						frm.set_df_property('stock_ledger_dimension', 'options', r.message);
-					} else {
-						frm.set_value("map_with_existing_field", 0);
-						frappe.msgprint(__('Inventory Dimensions not found'));
-					}
+			frm.fields.forEach((field) => {
+				if (field.df.fieldname !== 'disabled') {
+					frm.set_df_property(field.df.fieldname, "read_only", "1");
 				}
 			});
 		}
diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json
index 7e5df42..cfac5cd 100644
--- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json
+++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json
@@ -10,21 +10,22 @@
   "dimension_details_tab",
   "dimension_name",
   "reference_document",
+  "column_break_4",
   "disabled",
   "section_break_7",
   "field_mapping_section",
   "source_fieldname",
-  "target_fieldname",
   "column_break_9",
-  "map_with_existing_field",
-  "stock_ledger_dimension",
+  "target_fieldname",
   "applicable_for_documents_tab",
   "apply_to_all_doctypes",
   "document_type",
   "istable",
   "type_of_transaction",
   "column_break_16",
-  "condition"
+  "condition",
+  "applicable_condition_example_section",
+  "html_19"
  ],
  "fields": [
   {
@@ -81,7 +82,7 @@
    "label": "Applicable Condition"
   },
   {
-   "default": "1",
+   "default": "0",
    "fieldname": "apply_to_all_doctypes",
    "fieldtype": "Check",
    "label": "Apply to All Document Types"
@@ -115,31 +116,35 @@
    "label": "Field Mapping"
   },
   {
-   "default": "0",
-   "fieldname": "map_with_existing_field",
-   "fieldtype": "Check",
-   "label": "Map with existing field"
-  },
-  {
    "fieldname": "column_break_16",
    "fieldtype": "Column Break"
   },
   {
-   "depends_on": "map_with_existing_field",
-   "fieldname": "stock_ledger_dimension",
-   "fieldtype": "Select",
-   "label": "Stock Ledger Dimension"
-  },
-  {
    "fieldname": "type_of_transaction",
    "fieldtype": "Select",
    "label": "Type of Transaction",
    "options": "\nInward\nOutward"
+  },
+  {
+   "fieldname": "html_19",
+   "fieldtype": "HTML",
+   "options": "<table class=\"table table-bordered table-condensed\">\n<thead>\n  <tr>\n         <th class=\"table-sr\" style=\"width: 50%;\">Child Document</th>\n         <th class=\"table-sr\" style=\"width: 50%;\">Non Child Document</th>\n   </tr>\n</thead>\n<tbody>\n<tr>\n         <td>\n                  <p> To access parent document field use parent.fieldname and to access child table document field use doc.fieldname </p>\n\n         </td>\n         <td>\n                    <p>To access document field use doc.fieldname </p>\n         </td>\n</tr>\n<tr>\n        <td>\n                   <p><b>Example: </b> parent.doctype == \"Stock Entry\" and doc.item_code == \"Test\" </p>\n\n        </td>\n         <td>\n                   <p><b>Example: </b> doc.doctype == \"Stock Entry\" and doc.purpose == \"Manufacture\"</p>    \n          </td>\n</tr>\n\n</tbody>\n</table>\n\n\n\n\n\n\n"
+  },
+  {
+   "collapsible": 1,
+   "depends_on": "eval:!doc.apply_to_all_doctypes",
+   "fieldname": "applicable_condition_example_section",
+   "fieldtype": "Section Break",
+   "label": "Applicable Condition Examples"
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
   }
  ],
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2022-06-22 10:56:43.753713",
+ "modified": "2022-07-05 15:33:37.270373",
  "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 fd143da..3b9a84a 100644
--- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py
+++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py
@@ -2,36 +2,67 @@
 # For license information, please see license.txt
 
 import frappe
-from frappe import _, scrub
+from frappe import _, bold, scrub
 from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 from frappe.model.document import Document
 
 
+class DoNotChangeError(frappe.ValidationError):
+	pass
+
+
 class InventoryDimension(Document):
+	def onload(self):
+		if not self.is_new() and frappe.db.has_column("Stock Ledger Entry", self.target_fieldname):
+			self.set_onload("has_stock_ledger", self.has_stock_ledger())
+
+	def has_stock_ledger(self) -> str:
+		if not self.target_fieldname:
+			return
+
+		return frappe.get_all(
+			"Stock Ledger Entry", filters={self.target_fieldname: ("is", "set"), "is_cancelled": 0}, limit=1
+		)
+
 	def validate(self):
+		self.do_not_update_document()
 		self.reset_value()
 		self.validate_reference_document()
 		self.set_source_and_target_fieldname()
 
+	def do_not_update_document(self):
+		if self.is_new() or not self.has_stock_ledger():
+			return
+
+		old_doc = self._doc_before_save
+		for field in frappe.get_meta("Inventory Dimension").fields:
+			if field.fieldname != "disabled" and old_doc.get(field.fieldname) != self.get(field.fieldname):
+				msg = f"""The user can not change value of the field {bold(field.label)} because
+					stock transactions exists against the dimension {bold(self.name)}."""
+
+				frappe.throw(_(msg), DoNotChangeError)
+
 	def reset_value(self):
 		if self.apply_to_all_doctypes:
 			self.istable = 0
-			for field in ["document_type", "parent_field", "condition", "type_of_transaction"]:
+			for field in ["document_type", "condition"]:
 				self.set(field, None)
 
 	def validate_reference_document(self):
 		if frappe.get_cached_value("DocType", self.reference_document, "istable") == 1:
-			frappe.throw(_(f"The reference document {self.reference_document} can not be child table."))
+			msg = f"The reference document {self.reference_document} can not be child table."
+			frappe.throw(_(msg))
 
 		if self.reference_document in ["Batch", "Serial No", "Warehouse", "Item"]:
-			frappe.throw(
-				_(f"The reference document {self.reference_document} can not be an Inventory Dimension.")
-			)
+			msg = f"The reference document {self.reference_document} can not be an Inventory Dimension."
+			frappe.throw(_(msg))
 
-	def set_source_and_target_fieldname(self):
-		self.source_fieldname = scrub(self.dimension_name)
-		if not self.map_with_existing_field:
-			self.target_fieldname = self.source_fieldname
+	def set_source_and_target_fieldname(self) -> None:
+		if not self.source_fieldname:
+			self.source_fieldname = scrub(self.dimension_name)
+
+		if not self.target_fieldname:
+			self.target_fieldname = scrub(self.reference_document)
 
 	def on_update(self):
 		self.add_custom_fields()
@@ -107,7 +138,11 @@
 		) and sl_dict.actual_qty > 0:
 			continue
 
-		if frappe.safe_eval(row.condition, {"doc": doc, "parent_doc": parent_doc}):
+		evals = {"doc": doc}
+		if parent_doc:
+			evals["parent"] = parent_doc
+
+		if frappe.safe_eval(row.condition, evals):
 			return row
 
 
@@ -115,7 +150,7 @@
 	if not hasattr(frappe.local, "document_wise_inventory_dimensions"):
 		frappe.local.document_wise_inventory_dimensions = {}
 
-	if doctype not in frappe.local.document_wise_inventory_dimensions:
+	if not frappe.local.document_wise_inventory_dimensions.get(doctype):
 		dimensions = frappe.get_all(
 			"Inventory Dimension",
 			fields=["name", "source_fieldname", "condition", "target_fieldname", "type_of_transaction"],
@@ -129,20 +164,6 @@
 
 
 @frappe.whitelist()
-def get_source_fieldnames(reference_document, ignore_document):
-	return frappe.get_all(
-		"Inventory Dimension",
-		fields=["source_fieldname as value", "dimension_name as label"],
-		filters={
-			"disabled": 0,
-			"map_with_existing_field": 0,
-			"name": ("!=", ignore_document),
-			"reference_document": reference_document,
-		},
-	)
-
-
-@frappe.whitelist()
 def get_inventory_dimensions():
 	if not hasattr(frappe.local, "inventory_dimensions"):
 		frappe.local.inventory_dimensions = {}
@@ -152,10 +173,9 @@
 			"Inventory Dimension",
 			fields=[
 				"distinct target_fieldname as fieldname",
-				"dimension_name as label",
 				"reference_document as doctype",
 			],
-			filters={"disabled": 0, "map_with_existing_field": 0},
+			filters={"disabled": 0},
 		)
 
 		frappe.local.inventory_dimensions = dimensions
diff --git a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py
index 8d727b2..a79de1a 100644
--- a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py
+++ b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py
@@ -1,9 +1,112 @@
 # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 
-# import frappe
+import frappe
 from frappe.tests.utils import FrappeTestCase
 
+from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+
 
 class TestInventoryDimension(FrappeTestCase):
-	pass
+	def setUp(self):
+		prepare_test_data()
+
+	def test_inventory_dimension(self):
+		warehouse = "Shelf Warehouse - _TC"
+		item_code = "_Test Item"
+
+		create_inventory_dimension(
+			reference_document="Shelf",
+			type_of_transaction="Outward",
+			dimension_name="Shelf",
+			apply_to_all_doctypes=0,
+			document_type="Stock Entry Detail",
+			condition="parent.purpose == 'Material Issue'",
+		)
+
+		create_inventory_dimension(
+			reference_document="Shelf",
+			type_of_transaction="Inward",
+			dimension_name="To Shelf",
+			apply_to_all_doctypes=0,
+			document_type="Stock Entry Detail",
+			condition="parent.purpose == 'Material Receipt'",
+		)
+
+		inward = make_stock_entry(
+			item_code=item_code,
+			target=warehouse,
+			qty=5,
+			basic_rate=10,
+			do_not_save=True,
+			purpose="Material Receipt",
+		)
+
+		inward.items[0].to_shelf = "Shelf 1"
+		inward.save()
+		inward.submit()
+		inward.load_from_db()
+		print(inward.name)
+
+		sle_data = frappe.db.get_value(
+			"Stock Ledger Entry", {"voucher_no": inward.name}, ["shelf", "warehouse"], as_dict=1
+		)
+
+		self.assertEqual(inward.items[0].to_shelf, "Shelf 1")
+		self.assertEqual(sle_data.warehouse, warehouse)
+		self.assertEqual(sle_data.shelf, "Shelf 1")
+
+		outward = make_stock_entry(
+			item_code=item_code,
+			source=warehouse,
+			qty=3,
+			basic_rate=10,
+			do_not_save=True,
+			purpose="Material Issue",
+		)
+
+		outward.items[0].shelf = "Shelf 1"
+		outward.save()
+		outward.submit()
+		outward.load_from_db()
+
+		sle_shelf = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": outward.name}, "shelf")
+		self.assertEqual(sle_shelf, "Shelf 1")
+
+
+def prepare_test_data():
+	if not frappe.db.exists("DocType", "Shelf"):
+		frappe.get_doc(
+			{
+				"doctype": "DocType",
+				"name": "Shelf",
+				"module": "Stock",
+				"custom": 1,
+				"naming_rule": "By fieldname",
+				"autoname": "field:shelf_name",
+				"fields": [{"label": "Shelf Name", "fieldname": "shelf_name", "fieldtype": "Data"}],
+				"permissions": [
+					{"role": "System Manager", "permlevel": 0, "read": 1, "write": 1, "create": 1, "delete": 1}
+				],
+			}
+		).insert(ignore_permissions=True)
+
+	for shelf in ["Shelf 1", "Shelf 2"]:
+		if not frappe.db.exists("Shelf", shelf):
+			frappe.get_doc({"doctype": "Shelf", "shelf_name": shelf}).insert(ignore_permissions=True)
+
+	create_warehouse("Shelf Warehouse")
+
+
+def create_inventory_dimension(**args):
+	args = frappe._dict(args)
+
+	if frappe.db.exists("Inventory Dimension", args.dimension_name):
+		return frappe.get_doc("Inventory Dimension", args.dimension_name)
+
+	doc = frappe.new_doc("Inventory Dimension")
+	doc.update(args)
+	doc.insert(ignore_permissions=True)
+
+	return doc
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index 3492b05..a1e1030 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -146,7 +146,7 @@
 	for dimension in get_inventory_dimensions():
 		columns.append(
 			{
-				"label": _(dimension.label),
+				"label": _(dimension.doctype),
 				"fieldname": dimension.fieldname,
 				"fieldtype": "Link",
 				"options": dimension.doctype,
@@ -320,9 +320,8 @@
 
 	inventory_dimension_fields = get_inventory_dimension_fields()
 	if inventory_dimension_fields:
-		query = query.select(", ".join(inventory_dimension_fields))
-
 		for fieldname in inventory_dimension_fields:
+			query = query.select(fieldname)
 			if fieldname in filters and filters.get(fieldname):
 				query = query.where(sle[fieldname].isin(filters.get(fieldname)))
 
@@ -347,7 +346,7 @@
 	inventory_dimensions = get_inventory_dimension_fields()
 
 	for d in sle:
-		group_by_key = get_group_by_key(d, inventory_dimensions)
+		group_by_key = get_group_by_key(d, filters, inventory_dimensions)
 		if group_by_key not in iwb_map:
 			iwb_map[group_by_key] = frappe._dict(
 				{
@@ -399,16 +398,18 @@
 	return iwb_map
 
 
-def get_group_by_key(row, inventory_dimension_fields) -> tuple:
+def get_group_by_key(row, filters, inventory_dimension_fields) -> tuple:
 	group_by_key = [row.company, row.item_code, row.warehouse]
 
 	for fieldname in inventory_dimension_fields:
-		group_by_key.append(row.get(fieldname))
+		if filters.get(fieldname):
+			group_by_key.append(row.get(fieldname))
 
 	return tuple(group_by_key)
 
 
 def filter_items_with_no_transactions(iwb_map, float_precision: float, inventory_dimensions: list):
+	pop_keys = []
 	for group_by_key in iwb_map:
 		qty_dict = iwb_map[group_by_key]
 
@@ -423,7 +424,10 @@
 				no_transactions = False
 
 		if no_transactions:
-			iwb_map.pop(group_by_key)
+			pop_keys.append(group_by_key)
+
+	for key in pop_keys:
+		iwb_map.pop(key)
 
 	return iwb_map
 
diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py
index 1104983..807b800 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -224,7 +224,7 @@
 	for dimension in get_inventory_dimensions():
 		columns.append(
 			{
-				"label": _(dimension.label),
+				"label": _(dimension.doctype),
 				"fieldname": dimension.fieldname,
 				"fieldtype": "Link",
 				"options": dimension.doctype,