fix: allow to change rate manually in case of stand-alone credit note (#27036)

Co-authored-by: Marica <maricadsouza221197@gmail.com>
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index c3d83c7..053d42e 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1140,6 +1140,18 @@
 		self.assertEqual(loss_for_si['credit'], loss_for_return_si['debit'])
 		self.assertEqual(loss_for_si['debit'], loss_for_return_si['credit'])
 
+	def test_incoming_rate_for_stand_alone_credit_note(self):
+		return_si = create_sales_invoice(is_return=1, update_stock=1, qty=-1, rate=90000, incoming_rate=10,
+			company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', debit_to='Debtors - TCP1',
+			income_account='Sales - TCP1', expense_account='Cost of Goods Sold - TCP1', cost_center='Main - TCP1')
+
+		incoming_rate = frappe.db.get_value('Stock Ledger Entry', {'voucher_no': return_si.name}, 'incoming_rate')
+		debit_amount = frappe.db.get_value('GL Entry',
+			{'voucher_no': return_si.name, 'account': 'Stock In Hand - TCP1'}, 'debit')
+
+		self.assertEqual(debit_amount, 10.0)
+		self.assertEqual(incoming_rate, 10.0)
+
 	def test_discount_on_net_total(self):
 		si = frappe.copy_doc(test_records[2])
 		si.apply_discount_on = "Net Total"
@@ -2375,7 +2387,8 @@
 		"asset": args.asset or None,
 		"cost_center": args.cost_center or "_Test Cost Center - _TC",
 		"serial_no": args.serial_no,
-		"conversion_factor": 1
+		"conversion_factor": 1,
+		"incoming_rate": args.incoming_rate or 0
 	})
 
 	if not args.do_not_save:
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
index c77076c..b90f3f0 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -53,7 +53,6 @@
   "column_break_24",
   "base_net_rate",
   "base_net_amount",
-  "incoming_rate",
   "drop_ship",
   "delivered_by_supplier",
   "accounting",
@@ -81,6 +80,7 @@
   "target_warehouse",
   "quality_inspection",
   "batch_no",
+  "incoming_rate",
   "col_break5",
   "allow_zero_valuation_rate",
   "serial_no",
@@ -807,12 +807,12 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:parent.is_return && parent.update_stock && !parent.return_against",
    "fieldname": "incoming_rate",
    "fieldtype": "Currency",
-   "label": "Incoming Rate",
+   "label": "Incoming Rate (Costing)",
    "no_copy": 1,
-   "print_hide": 1,
-   "read_only": 1
+   "print_hide": 1
   },
   {
    "depends_on": "eval: doc.uom != doc.stock_uom",
@@ -833,7 +833,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-08-12 20:15:47.668399",
+ "modified": "2021-08-19 13:41:53.435827",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice Item",
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 5ee1f2f..01486fc 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -394,19 +394,6 @@
 	if not return_against:
 		return_against = frappe.get_cached_value(voucher_type, voucher_no, "return_against")
 
-	if not return_against and voucher_type == 'Sales Invoice' and sle:
-		return get_incoming_rate({
-			"item_code": sle.item_code,
-			"warehouse": sle.warehouse,
-			"posting_date": sle.get('posting_date'),
-			"posting_time": sle.get('posting_time'),
-			"qty": sle.actual_qty,
-			"serial_no": sle.get('serial_no'),
-			"company": sle.company,
-			"voucher_type": sle.voucher_type,
-			"voucher_no": sle.voucher_no
-		}, raise_error_if_no_rate=False)
-
 	return_against_item_field = get_return_against_item_fields(voucher_type)
 
 	filters = get_filters(voucher_type, voucher_no, voucher_detail_no,
@@ -417,7 +404,24 @@
 	else:
 		select_field = "abs(stock_value_difference / actual_qty)"
 
-	return flt(frappe.db.get_value("Stock Ledger Entry", filters, select_field))
+	rate = flt(frappe.db.get_value("Stock Ledger Entry", filters, select_field))
+	if not (rate and return_against) and voucher_type in ['Sales Invoice', 'Delivery Note']:
+		rate = frappe.db.get_value(f'{voucher_type} Item', voucher_detail_no, 'incoming_rate')
+
+		if not rate and sle:
+			rate = get_incoming_rate({
+				"item_code": sle.item_code,
+				"warehouse": sle.warehouse,
+				"posting_date": sle.get('posting_date'),
+				"posting_time": sle.get('posting_time'),
+				"qty": sle.actual_qty,
+				"serial_no": sle.get('serial_no'),
+				"company": sle.company,
+				"voucher_type": sle.voucher_type,
+				"voucher_no": sle.voucher_no
+			}, raise_error_if_no_rate=False)
+
+	return rate
 
 def get_return_against_item_fields(voucher_type):
 	return_against_item_fields = {
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index fc2cc97..4ea0e11 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -362,7 +362,7 @@
 				sales_order.update_reserved_qty(so_item_rows)
 
 	def set_incoming_rate(self):
-		if self.doctype not in ("Delivery Note", "Sales Invoice", "Sales Order"):
+		if self.doctype not in ("Delivery Note", "Sales Invoice"):
 			return
 
 		items = self.get("items") + (self.get("packed_items") or [])
@@ -371,18 +371,19 @@
 				# Get incoming rate based on original item cost based on valuation method
 				qty = flt(d.get('stock_qty') or d.get('actual_qty'))
 
-				d.incoming_rate = get_incoming_rate({
-					"item_code": d.item_code,
-					"warehouse": d.warehouse,
-					"posting_date": self.get('posting_date') or self.get('transaction_date'),
-					"posting_time": self.get('posting_time') or nowtime(),
-					"qty": qty if cint(self.get("is_return")) else (-1 * qty),
-					"serial_no": d.get('serial_no'),
-					"company": self.company,
-					"voucher_type": self.doctype,
-					"voucher_no": self.name,
-					"allow_zero_valuation": d.get("allow_zero_valuation")
-				}, raise_error_if_no_rate=False)
+				if not d.incoming_rate:
+					d.incoming_rate = get_incoming_rate({
+						"item_code": d.item_code,
+						"warehouse": d.warehouse,
+						"posting_date": self.get('posting_date') or self.get('transaction_date'),
+						"posting_time": self.get('posting_time') or nowtime(),
+						"qty": qty if cint(self.get("is_return")) else (-1 * qty),
+						"serial_no": d.get('serial_no'),
+						"company": self.company,
+						"voucher_type": self.doctype,
+						"voucher_no": self.name,
+						"allow_zero_valuation": d.get("allow_zero_valuation")
+					}, raise_error_if_no_rate=False)
 
 				# For internal transfers use incoming rate as the valuation rate
 				if self.is_internal_transfer():
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index eddd048..27feec1 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -332,6 +332,7 @@
 			where
 				item_code = %(item_code)s
 				and warehouse = %(warehouse)s
+				and is_cancelled = 0
 				and timestamp(posting_date, time_format(posting_time, %(time_format)s)) = timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
 
 			order by