refactor: move scan api to stock utils; add item_info
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 4f1891f..d40218e 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -3,6 +3,7 @@
 
 
 import json
+from typing import Dict, Optional
 
 import frappe
 from frappe import _
@@ -548,3 +549,51 @@
 		)
 
 	return bool(reposting_pending)
+
+
+@frappe.whitelist()
+def scan_barcode(search_value: str) -> Dict[str, Optional[str]]:
+
+	# search barcode no
+	barcode_data = frappe.db.get_value(
+		"Item Barcode",
+		{"barcode": search_value},
+		["barcode", "parent as item_code"],
+		as_dict=True,
+	)
+	if barcode_data:
+		return _update_item_info(barcode_data)
+
+	# search serial no
+	serial_no_data = frappe.db.get_value(
+		"Serial No",
+		search_value,
+		["name as serial_no", "item_code", "batch_no"],
+		as_dict=True,
+	)
+	if serial_no_data:
+		return _update_item_info(serial_no_data)
+
+	# search batch no
+	batch_no_data = frappe.db.get_value(
+		"Batch",
+		search_value,
+		["name as batch_no", "item as item_code"],
+		as_dict=True,
+	)
+	if batch_no_data:
+		return _update_item_info(batch_no_data)
+
+	return {}
+
+
+def _update_item_info(scan_result: Dict[str, Optional[str]]) -> Dict[str, Optional[str]]:
+	if item_code := scan_result.get("item_code"):
+		if item_info := frappe.get_cached_value(
+			"Item",
+			item_code,
+			["has_batch_no", "has_serial_no"],
+			as_dict=True,
+		):
+			scan_result.update(item_info)
+	return scan_result