fix: inventory dimension for material transfer not working
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index a27e348..796a069 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -442,7 +442,29 @@
 			if not dimension:
 				continue
 
-			if row.get(dimension.source_fieldname):
+			if self.doctype in [
+				"Purchase Invoice",
+				"Purchase Receipt",
+				"Sales Invoice",
+				"Delivery Note",
+				"Stock Entry",
+			]:
+				if (sl_dict.actual_qty > 0 and self.doctype in ["Purchase Invoice", "Purchase Receipt"]) or (
+					sl_dict.actual_qty < 0 and self.doctype in ["Sales Invoice", "Delivery Note", "Stock Entry"]
+				):
+					sl_dict[dimension.target_fieldname] = row.get(dimension.source_fieldname)
+				else:
+					fieldname_start_with = "to"
+					if self.doctype in ["Purchase Invoice", "Purchase Receipt"]:
+						fieldname_start_with = "from"
+
+					fieldname = f"{fieldname_start_with}_{dimension.source_fieldname}"
+					sl_dict[dimension.target_fieldname] = row.get(fieldname)
+
+					if not sl_dict.get(dimension.target_fieldname):
+						sl_dict[dimension.target_fieldname] = row.get(dimension.source_fieldname)
+
+			elif row.get(dimension.source_fieldname):
 				sl_dict[dimension.target_fieldname] = row.get(dimension.source_fieldname)
 
 			if not sl_dict.get(dimension.target_fieldname) and dimension.fetch_from_parent:
diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py
index db2b5d0..8bff4d5 100644
--- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py
+++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py
@@ -75,7 +75,16 @@
 		self.delete_custom_fields()
 
 	def delete_custom_fields(self):
-		filters = {"fieldname": self.source_fieldname}
+		filters = {
+			"fieldname": (
+				"in",
+				[
+					self.source_fieldname,
+					f"to_{self.source_fieldname}",
+					f"from_{self.source_fieldname}",
+				],
+			)
+		}
 
 		if self.document_type:
 			filters["dt"] = self.document_type
@@ -88,6 +97,8 @@
 
 	def reset_value(self):
 		if self.apply_to_all_doctypes:
+			self.type_of_transaction = ""
+
 			self.istable = 0
 			for field in ["document_type", "condition"]:
 				self.set(field, None)
@@ -111,12 +122,35 @@
 	def on_update(self):
 		self.add_custom_fields()
 
-	def add_custom_fields(self):
-		dimension_fields = [
+	@staticmethod
+	def get_insert_after_fieldname(doctype):
+		return frappe.get_all(
+			"DocField",
+			fields=["fieldname"],
+			filters={"parent": doctype},
+			order_by="idx desc",
+			limit=1,
+		)[0].fieldname
+
+	def get_dimension_fields(self, doctype=None):
+		if not doctype:
+			doctype = self.document_type
+
+		label_start_with = ""
+		if doctype in ["Purchase Invoice Item", "Purchase Receipt Item"]:
+			label_start_with = "Target"
+		elif doctype in ["Sales Invoice Item", "Delivery Note Item", "Stock Entry Detail"]:
+			label_start_with = "Source"
+
+		label = self.dimension_name
+		if label_start_with:
+			label = f"{label_start_with} {self.dimension_name}"
+
+		return [
 			dict(
 				fieldname="inventory_dimension",
 				fieldtype="Section Break",
-				insert_after="warehouse",
+				insert_after=self.get_insert_after_fieldname(doctype),
 				label="Inventory Dimension",
 				collapsible=1,
 			),
@@ -125,24 +159,37 @@
 				fieldtype="Link",
 				insert_after="inventory_dimension",
 				options=self.reference_document,
-				label=self.dimension_name,
+				label=label,
 				reqd=self.reqd,
 				mandatory_depends_on=self.mandatory_depends_on,
 			),
 		]
 
+	def add_custom_fields(self):
 		custom_fields = {}
 
+		dimension_fields = []
 		if self.apply_to_all_doctypes:
 			for doctype in get_inventory_documents():
-				if not field_exists(doctype[0], self.source_fieldname):
-					custom_fields.setdefault(doctype[0], dimension_fields)
+				if field_exists(doctype[0], self.source_fieldname):
+					continue
+
+				dimension_fields = self.get_dimension_fields(doctype[0])
+				self.add_transfer_field(doctype[0], dimension_fields)
+				custom_fields.setdefault(doctype[0], dimension_fields)
 		elif not field_exists(self.document_type, self.source_fieldname):
+			dimension_fields = self.get_dimension_fields()
+
+			self.add_transfer_field(self.document_type, dimension_fields)
 			custom_fields.setdefault(self.document_type, dimension_fields)
 
-		if not frappe.db.get_value(
-			"Custom Field", {"dt": "Stock Ledger Entry", "fieldname": self.target_fieldname}
-		) and not field_exists("Stock Ledger Entry", self.target_fieldname):
+		if (
+			dimension_fields
+			and not frappe.db.get_value(
+				"Custom Field", {"dt": "Stock Ledger Entry", "fieldname": self.target_fieldname}
+			)
+			and not field_exists("Stock Ledger Entry", self.target_fieldname)
+		):
 			dimension_field = dimension_fields[1]
 			dimension_field["mandatory_depends_on"] = ""
 			dimension_field["reqd"] = 0
@@ -152,6 +199,53 @@
 		if custom_fields:
 			create_custom_fields(custom_fields)
 
+	def add_transfer_field(self, doctype, dimension_fields):
+		if doctype not in [
+			"Stock Entry Detail",
+			"Sales Invoice Item",
+			"Delivery Note Item",
+			"Purchase Invoice Item",
+			"Purchase Receipt Item",
+		]:
+			return
+
+		fieldname_start_with = "to"
+		label_start_with = "Target"
+		display_depends_on = ""
+
+		if doctype in ["Purchase Invoice Item", "Purchase Receipt Item"]:
+			fieldname_start_with = "from"
+			label_start_with = "Source"
+			display_depends_on = "eval:parent.is_internal_supplier == 1"
+		elif doctype != "Stock Entry Detail":
+			display_depends_on = "eval:parent.is_internal_customer == 1"
+		elif doctype == "Stock Entry Detail":
+			display_depends_on = "eval:parent.purpose != 'Material Issue'"
+
+		fieldname = f"{fieldname_start_with}_{self.source_fieldname}"
+		label = f"{label_start_with} {self.dimension_name}"
+
+		if field_exists(doctype, fieldname):
+			return
+
+		dimension_fields.extend(
+			[
+				dict(
+					fieldname="inventory_dimension_col_break",
+					fieldtype="Column Break",
+					insert_after=self.source_fieldname,
+				),
+				dict(
+					fieldname=fieldname,
+					fieldtype="Link",
+					insert_after="inventory_dimension_col_break",
+					options=self.reference_document,
+					label=label,
+					depends_on=display_depends_on,
+				),
+			]
+		)
+
 
 def field_exists(doctype, fieldname) -> str or None:
 	return frappe.db.get_value("DocField", {"parent": doctype, "fieldname": fieldname}, "name")
@@ -185,18 +279,19 @@
 	dimensions = get_document_wise_inventory_dimensions(doc.doctype)
 	filter_dimensions = []
 	for row in dimensions:
-		if (
-			row.type_of_transaction == "Inward"
-			if doc.docstatus == 1
-			else row.type_of_transaction != "Inward"
-		) and sl_dict.actual_qty < 0:
-			continue
-		elif (
-			row.type_of_transaction == "Outward"
-			if doc.docstatus == 1
-			else row.type_of_transaction != "Outward"
-		) and sl_dict.actual_qty > 0:
-			continue
+		if row.type_of_transaction:
+			if (
+				row.type_of_transaction == "Inward"
+				if doc.docstatus == 1
+				else row.type_of_transaction != "Inward"
+			) and sl_dict.actual_qty < 0:
+				continue
+			elif (
+				row.type_of_transaction == "Outward"
+				if doc.docstatus == 1
+				else row.type_of_transaction != "Outward"
+			) and sl_dict.actual_qty > 0:
+				continue
 
 		evals = {"doc": doc}
 		if parent_doc:
diff --git a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py
index 28b1ed9..ae5f521 100644
--- a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py
+++ b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py
@@ -12,6 +12,7 @@
 	DoNotChangeError,
 	delete_dimension,
 )
+from erpnext.stock.doctype.item.test_item import create_item
 from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
@@ -20,6 +21,7 @@
 class TestInventoryDimension(FrappeTestCase):
 	def setUp(self):
 		prepare_test_data()
+		create_store_dimension()
 
 	def test_validate_inventory_dimension(self):
 		# Can not be child doc
@@ -73,6 +75,8 @@
 		self.assertFalse(custom_field)
 
 	def test_inventory_dimension(self):
+		frappe.local.document_wise_inventory_dimensions = {}
+
 		warehouse = "Shelf Warehouse - _TC"
 		item_code = "_Test Item"
 
@@ -143,6 +147,8 @@
 		self.assertRaises(DoNotChangeError, inv_dim1.save)
 
 	def test_inventory_dimension_for_purchase_receipt_and_delivery_note(self):
+		frappe.local.document_wise_inventory_dimensions = {}
+
 		inv_dimension = create_inventory_dimension(
 			reference_document="Rack", dimension_name="Rack", apply_to_all_doctypes=1
 		)
@@ -250,6 +256,97 @@
 			)
 		)
 
+	def test_for_purchase_sales_and_stock_transaction(self):
+		create_inventory_dimension(
+			reference_document="Store",
+			type_of_transaction="Outward",
+			dimension_name="Store",
+			apply_to_all_doctypes=1,
+		)
+
+		item_code = "Test Inventory Dimension Item"
+		create_item(item_code)
+		warehouse = create_warehouse("Store Warehouse")
+
+		# Purchase Receipt -> Inward in Store 1
+		pr_doc = make_purchase_receipt(
+			item_code=item_code, warehouse=warehouse, qty=10, rate=100, do_not_submit=True
+		)
+
+		pr_doc.items[0].store = "Store 1"
+		pr_doc.save()
+		pr_doc.submit()
+
+		entries = get_voucher_sl_entries(pr_doc.name, ["warehouse", "store", "incoming_rate"])
+
+		self.assertEqual(entries[0].warehouse, warehouse)
+		self.assertEqual(entries[0].store, "Store 1")
+
+		# Stock Entry -> Transfer from Store 1 to Store 2
+		se_doc = make_stock_entry(
+			item_code=item_code, qty=10, from_warehouse=warehouse, to_warehouse=warehouse, do_not_save=True
+		)
+
+		se_doc.items[0].store = "Store 1"
+		se_doc.items[0].to_store = "Store 2"
+
+		se_doc.save()
+		se_doc.submit()
+
+		entries = get_voucher_sl_entries(
+			se_doc.name, ["warehouse", "store", "incoming_rate", "actual_qty"]
+		)
+
+		for entry in entries:
+			self.assertEqual(entry.warehouse, warehouse)
+			if entry.actual_qty > 0:
+				self.assertEqual(entry.store, "Store 2")
+				self.assertEqual(entry.incoming_rate, 100.0)
+			else:
+				self.assertEqual(entry.store, "Store 1")
+
+		# Delivery Note -> Outward from Store 2
+
+		dn_doc = create_delivery_note(item_code=item_code, qty=10, warehouse=warehouse, do_not_save=True)
+
+		dn_doc.items[0].store = "Store 2"
+		dn_doc.save()
+		dn_doc.submit()
+
+		entries = get_voucher_sl_entries(dn_doc.name, ["warehouse", "store", "actual_qty"])
+
+		self.assertEqual(entries[0].warehouse, warehouse)
+		self.assertEqual(entries[0].store, "Store 2")
+		self.assertEqual(entries[0].actual_qty, -10.0)
+
+
+def get_voucher_sl_entries(voucher_no, fields):
+	return frappe.get_all(
+		"Stock Ledger Entry", filters={"voucher_no": voucher_no}, fields=fields, order_by="creation"
+	)
+
+
+def create_store_dimension():
+	if not frappe.db.exists("DocType", "Store"):
+		frappe.get_doc(
+			{
+				"doctype": "DocType",
+				"name": "Store",
+				"module": "Stock",
+				"custom": 1,
+				"naming_rule": "By fieldname",
+				"autoname": "field:store_name",
+				"fields": [{"label": "Store Name", "fieldname": "store_name", "fieldtype": "Data"}],
+				"permissions": [
+					{"role": "System Manager", "permlevel": 0, "read": 1, "write": 1, "create": 1, "delete": 1}
+				],
+			}
+		).insert(ignore_permissions=True)
+
+	for store in ["Store 1", "Store 2"]:
+		if not frappe.db.exists("Store", store):
+			frappe.get_doc({"doctype": "Store", "store_name": store}).insert(ignore_permissions=True)
+
 
 def prepare_test_data():
 	if not frappe.db.exists("DocType", "Shelf"):