fix: cannot merge pos invoice if validate selling price is checked (#23593)

* fix: cannot merge pos invoice if validate selling price is checked

* fix: validate selling price

* fix: test

* chore: add test

* fix: error message
diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
index 1a5920d..e08af95 100644
--- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
@@ -7,6 +7,7 @@
 import unittest, copy, time
 from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
 from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
+from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 
 class TestPOSInvoice(unittest.TestCase):
 	def test_timestamp_change(self):
@@ -307,8 +308,9 @@
 		merge_pos_invoices()
 
 		pos_inv.load_from_db()
-		sales_invoice = frappe.get_doc("Sales Invoice", pos_inv.consolidated_invoice)
-		self.assertEqual(sales_invoice.grand_total, 3500)
+		rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
+		self.assertEqual(rounded_total, 3500)
+		frappe.set_user("Administrator")
 	
 	def test_merging_into_sales_invoice_with_discount_and_inclusive_tax(self):
 		from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
@@ -348,8 +350,55 @@
 		merge_pos_invoices()
 
 		pos_inv.load_from_db()
-		sales_invoice = frappe.get_doc("Sales Invoice", pos_inv.consolidated_invoice)
-		self.assertEqual(sales_invoice.rounded_total, 840)
+		rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
+		self.assertEqual(rounded_total, 840)
+		frappe.set_user("Administrator")
+
+	def test_merging_with_validate_selling_price(self):
+		from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
+		from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices
+
+		if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
+			frappe.db.set_value("Selling Settings", "Selling Settings", "validate_selling_price", 1)
+
+		make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=1, basic_rate=300)
+		frappe.db.sql("delete from `tabPOS Invoice`")
+		test_user, pos_profile = init_user_and_profile()
+		pos_inv = create_pos_invoice(rate=300, do_not_submit=1)
+		pos_inv.append('payments', {
+			'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
+		})
+		pos_inv.append('taxes', {
+			"charge_type": "On Net Total",
+			"account_head": "_Test Account Service Tax - _TC",
+			"cost_center": "_Test Cost Center - _TC",
+			"description": "Service Tax",
+			"rate": 14,
+			'included_in_print_rate': 1
+		})
+		self.assertRaises(frappe.ValidationError, pos_inv.submit)
+
+		pos_inv2 = create_pos_invoice(rate=400, do_not_submit=1)
+		pos_inv2.append('payments', {
+			'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 400
+		})
+		pos_inv2.append('taxes', {
+			"charge_type": "On Net Total",
+			"account_head": "_Test Account Service Tax - _TC",
+			"cost_center": "_Test Cost Center - _TC",
+			"description": "Service Tax",
+			"rate": 14,
+			'included_in_print_rate': 1
+		})
+		pos_inv2.submit()
+
+		merge_pos_invoices()
+
+		pos_inv2.load_from_db()
+		rounded_total = frappe.db.get_value("Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total")
+		self.assertEqual(rounded_total, 400)
+		frappe.set_user("Administrator")
+		frappe.db.set_value("Selling Settings", "Selling Settings", "validate_selling_price", 0)
 
 def create_pos_invoice(**args):
 	args = frappe._dict(args)
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 0ae1759..5886171 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -3,7 +3,7 @@
 
 from __future__ import unicode_literals
 import frappe
-from frappe.utils import cint, flt, cstr, comma_or
+from frappe.utils import cint, flt, cstr, comma_or, get_link_to_form
 from frappe import _, throw
 from erpnext.stock.get_item_details import get_bin_details
 from erpnext.stock.utils import get_incoming_rate
@@ -173,22 +173,26 @@
 
 	def validate_selling_price(self):
 		def throw_message(idx, item_name, rate, ref_rate_field):
-			frappe.throw(_("""Row #{}: Selling rate for item {} is lower than its {}. Selling rate should be atleast {}""")
-				.format(idx, item_name, ref_rate_field, rate))
+			bold_net_rate = frappe.bold("net rate")
+			msg = (_("""Row #{}: Selling rate for item {} is lower than its {}. Selling {} should be atleast {}""")
+						.format(idx, frappe.bold(item_name), frappe.bold(ref_rate_field), bold_net_rate, frappe.bold(rate)))
+			msg += "<br><br>"
+			msg += (_("""You can alternatively disable selling price validation in {} to bypass this validation.""")
+						.format(get_link_to_form("Selling Settings", "Selling Settings")))
+			frappe.throw(msg, title=_("Invalid Selling Price"))
 
 		if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
 			return
-
 		if hasattr(self, "is_return") and self.is_return:
 			return
 
 		for it in self.get("items"):
 			if not it.item_code:
 				continue
-
+			
 			last_purchase_rate, is_stock_item = frappe.get_cached_value("Item", it.item_code, ["last_purchase_rate", "is_stock_item"])
-			last_purchase_rate_in_sales_uom = last_purchase_rate / (it.conversion_factor or 1)
-			if flt(it.base_rate) < flt(last_purchase_rate_in_sales_uom):
+			last_purchase_rate_in_sales_uom = last_purchase_rate * (it.conversion_factor or 1)
+			if flt(it.base_net_rate) < flt(last_purchase_rate_in_sales_uom):
 				throw_message(it.idx, frappe.bold(it.item_name), last_purchase_rate_in_sales_uom, "last purchase rate")
 
 			last_valuation_rate = frappe.db.sql("""
@@ -197,8 +201,8 @@
 				ORDER BY posting_date DESC, posting_time DESC, creation DESC LIMIT 1
 				""", (it.item_code, it.warehouse))
 			if last_valuation_rate:
-				last_valuation_rate_in_sales_uom = last_valuation_rate[0][0] / (it.conversion_factor or 1)
-				if is_stock_item and flt(it.base_rate) < flt(last_valuation_rate_in_sales_uom) \
+				last_valuation_rate_in_sales_uom = last_valuation_rate[0][0] * (it.conversion_factor or 1)
+				if is_stock_item and flt(it.base_net_rate) < flt(last_valuation_rate_in_sales_uom) \
 					and not self.get('is_internal_customer'):
 					throw_message(it.idx, frappe.bold(it.item_name), last_valuation_rate_in_sales_uom, "valuation rate")