Merge branch 'develop' into FIX-ISS-22-23-05720
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index d60feb2..01bf2e4 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -6,7 +6,7 @@
 from functools import partial
 
 import frappe
-from frappe.tests.utils import FrappeTestCase
+from frappe.tests.utils import FrappeTestCase, timeout
 from frappe.utils import cstr, flt
 
 from erpnext.controllers.tests.test_subcontracting_controller import (
@@ -27,6 +27,7 @@
 
 
 class TestBOM(FrappeTestCase):
+	@timeout
 	def test_get_items(self):
 		from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
 
@@ -37,6 +38,7 @@
 		self.assertTrue(test_records[2]["items"][1]["item_code"] in items_dict)
 		self.assertEqual(len(items_dict.values()), 2)
 
+	@timeout
 	def test_get_items_exploded(self):
 		from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
 
@@ -49,11 +51,13 @@
 		self.assertTrue(test_records[0]["items"][1]["item_code"] in items_dict)
 		self.assertEqual(len(items_dict.values()), 3)
 
+	@timeout
 	def test_get_items_list(self):
 		from erpnext.manufacturing.doctype.bom.bom import get_bom_items
 
 		self.assertEqual(len(get_bom_items(bom=get_default_bom(), company="_Test Company")), 3)
 
+	@timeout
 	def test_default_bom(self):
 		def _get_default_bom_in_item():
 			return cstr(frappe.db.get_value("Item", "_Test FG Item 2", "default_bom"))
@@ -71,6 +75,7 @@
 
 		self.assertTrue(_get_default_bom_in_item(), bom.name)
 
+	@timeout
 	def test_update_bom_cost_in_all_boms(self):
 		# get current rate for '_Test Item 2'
 		bom_rates = frappe.db.get_values(
@@ -99,6 +104,7 @@
 		):
 			self.assertEqual(d.base_rate, rm_base_rate + 10)
 
+	@timeout
 	def test_bom_cost(self):
 		bom = frappe.copy_doc(test_records[2])
 		bom.insert()
@@ -127,6 +133,7 @@
 		self.assertAlmostEqual(bom.base_raw_material_cost, base_raw_material_cost)
 		self.assertAlmostEqual(bom.base_total_cost, base_raw_material_cost + base_op_cost)
 
+	@timeout
 	def test_bom_cost_with_batch_size(self):
 		bom = frappe.copy_doc(test_records[2])
 		bom.docstatus = 0
@@ -145,6 +152,7 @@
 		self.assertAlmostEqual(bom.operating_cost, op_cost / 2)
 		bom.delete()
 
+	@timeout
 	def test_bom_cost_multi_uom_multi_currency_based_on_price_list(self):
 		frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependent", 1)
 		for item_code, rate in (("_Test Item", 3600), ("_Test Item Home Desktop Manufactured", 3000)):
@@ -181,6 +189,7 @@
 		self.assertEqual(bom.base_raw_material_cost, 27000)
 		self.assertEqual(bom.base_total_cost, 33000)
 
+	@timeout
 	def test_bom_cost_multi_uom_based_on_valuation_rate(self):
 		bom = frappe.copy_doc(test_records[2])
 		bom.set_rate_of_sub_assembly_item_based_on_bom = 0
@@ -202,6 +211,7 @@
 
 		self.assertEqual(bom.items[0].rate, 20)
 
+	@timeout
 	def test_bom_cost_with_fg_based_operating_cost(self):
 		bom = frappe.copy_doc(test_records[4])
 		bom.insert()
@@ -229,6 +239,7 @@
 		self.assertAlmostEqual(bom.base_raw_material_cost, base_raw_material_cost)
 		self.assertAlmostEqual(bom.base_total_cost, base_raw_material_cost + base_op_cost)
 
+	@timeout
 	def test_subcontractor_sourced_item(self):
 		item_code = "_Test Subcontracted FG Item 1"
 		set_backflush_based_on("Material Transferred for Subcontract")
@@ -310,6 +321,7 @@
 		supplied_items = sorted([d.rm_item_code for d in sco.supplied_items])
 		self.assertEqual(bom_items, supplied_items)
 
+	@timeout
 	def test_bom_tree_representation(self):
 		bom_tree = {
 			"Assembly": {
@@ -335,6 +347,7 @@
 		for reqd_item, created_item in zip(reqd_order, created_order):
 			self.assertEqual(reqd_item, created_item.item_code)
 
+	@timeout
 	def test_generated_variant_bom(self):
 		from erpnext.controllers.item_variant import create_variant
 
@@ -375,6 +388,7 @@
 			self.assertEqual(reqd_item.qty, created_item.qty)
 			self.assertEqual(reqd_item.exploded_qty, created_item.exploded_qty)
 
+	@timeout
 	def test_bom_recursion_1st_level(self):
 		"""BOM should not allow BOM item again in child"""
 		item_code = make_item(properties={"is_stock_item": 1}).name
@@ -387,6 +401,7 @@
 			bom.items[0].bom_no = bom.name
 			bom.save()
 
+	@timeout
 	def test_bom_recursion_transitive(self):
 		item1 = make_item(properties={"is_stock_item": 1}).name
 		item2 = make_item(properties={"is_stock_item": 1}).name
@@ -408,6 +423,7 @@
 			bom1.save()
 			bom2.save()
 
+	@timeout
 	def test_bom_with_process_loss_item(self):
 		fg_item_non_whole, fg_item_whole, bom_item = create_process_loss_bom_items()
 
@@ -421,6 +437,7 @@
 		#  Items with whole UOMs can't be PL Items
 		self.assertRaises(frappe.ValidationError, bom_doc.submit)
 
+	@timeout
 	def test_bom_item_query(self):
 		query = partial(
 			item_query,
@@ -440,6 +457,7 @@
 		)
 		self.assertTrue(0 < len(filtered) <= 3, msg="Item filtering showing excessive results")
 
+	@timeout
 	def test_exclude_exploded_items_from_bom(self):
 		bom_no = get_default_bom()
 		new_bom = frappe.copy_doc(frappe.get_doc("BOM", bom_no))
@@ -458,6 +476,7 @@
 
 		new_bom.delete()
 
+	@timeout
 	def test_valid_transfer_defaults(self):
 		bom_with_op = frappe.db.get_value(
 			"BOM", {"item": "_Test FG Item 2", "with_operations": 1, "is_active": 1}
@@ -489,11 +508,13 @@
 		self.assertEqual(bom.transfer_material_against, "Work Order")
 		bom.delete()
 
+	@timeout
 	def test_bom_name_length(self):
 		"""test >140 char names"""
 		bom_tree = {"x" * 140: {" ".join(["abc"] * 35): {}}}
 		create_nested_bom(bom_tree, prefix="")
 
+	@timeout
 	def test_version_index(self):
 
 		bom = frappe.new_doc("BOM")
@@ -515,6 +536,7 @@
 					msg=f"Incorrect index for {existing_boms}",
 				)
 
+	@timeout
 	def test_bom_versioning(self):
 		bom_tree = {frappe.generate_hash(length=10): {frappe.generate_hash(length=10): {}}}
 		bom = create_nested_bom(bom_tree, prefix="")
@@ -547,6 +569,7 @@
 		self.assertNotEqual(amendment.name, version.name)
 		self.assertEqual(int(version.name.split("-")[-1]), 2)
 
+	@timeout
 	def test_clear_inpection_quality(self):
 
 		bom = frappe.copy_doc(test_records[2], ignore_no_copy=True)
@@ -565,6 +588,7 @@
 
 		self.assertEqual(bom.quality_inspection_template, None)
 
+	@timeout
 	def test_bom_pricing_based_on_lpp(self):
 		from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
 
@@ -585,6 +609,7 @@
 		bom.submit()
 		self.assertEqual(bom.items[0].rate, 42)
 
+	@timeout
 	def test_set_default_bom_for_item_having_single_bom(self):
 		from erpnext.stock.doctype.item.test_item import make_item
 
@@ -621,6 +646,7 @@
 		bom.reload()
 		self.assertEqual(frappe.get_value("Item", fg_item.item_code, "default_bom"), bom.name)
 
+	@timeout
 	def test_exploded_items_rate(self):
 		rm_item = make_item(
 			properties={"is_stock_item": 1, "valuation_rate": 99, "last_purchase_rate": 89}
@@ -649,6 +675,7 @@
 		bom.submit()
 		self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate)
 
+	@timeout
 	def test_bom_cost_update_flag(self):
 		rm_item = make_item(
 			properties={"is_stock_item": 1, "valuation_rate": 99, "last_purchase_rate": 89}
diff --git a/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py
index 5dd557f..2026f62 100644
--- a/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py
+++ b/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py
@@ -2,7 +2,7 @@
 # License: GNU General Public License v3. See license.txt
 
 import frappe
-from frappe.tests.utils import FrappeTestCase
+from frappe.tests.utils import FrappeTestCase, timeout
 
 from erpnext.manufacturing.doctype.bom_update_log.test_bom_update_log import (
 	update_cost_in_all_boms_in_test,
@@ -20,6 +20,7 @@
 	def tearDown(self):
 		frappe.db.rollback()
 
+	@timeout
 	def test_replace_bom(self):
 		current_bom = "BOM-_Test Item Home Desktop Manufactured-001"
 
@@ -33,6 +34,7 @@
 		self.assertFalse(frappe.db.exists("BOM Item", {"bom_no": current_bom, "docstatus": 1}))
 		self.assertTrue(frappe.db.exists("BOM Item", {"bom_no": bom_doc.name, "docstatus": 1}))
 
+	@timeout
 	def test_bom_cost(self):
 		for item in ["BOM Cost Test Item 1", "BOM Cost Test Item 2", "BOM Cost Test Item 3"]:
 			item_doc = create_item(item, valuation_rate=100)
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index c06700a..05a37ee 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -377,7 +377,9 @@
 						"" if item_barcode.barcode_type not in options else item_barcode.barcode_type
 					)
 					if item_barcode.barcode_type:
-						barcode_type = convert_erpnext_to_barcodenumber(item_barcode.barcode_type.upper())
+						barcode_type = convert_erpnext_to_barcodenumber(
+							item_barcode.barcode_type.upper(), item_barcode.barcode
+						)
 						if barcode_type in barcodenumber.barcodes():
 							if not barcodenumber.check_code(barcode_type, item_barcode.barcode):
 								frappe.throw(
@@ -982,20 +984,29 @@
 				)
 
 
-def convert_erpnext_to_barcodenumber(erpnext_number):
+def convert_erpnext_to_barcodenumber(erpnext_number, barcode):
+	if erpnext_number == "EAN":
+		ean_type = {
+			8: "EAN8",
+			13: "EAN13",
+		}
+		barcode_length = len(barcode)
+		if barcode_length in ean_type:
+			return ean_type[barcode_length]
+
+		return erpnext_number
+
 	convert = {
 		"UPC-A": "UPCA",
 		"CODE-39": "CODE39",
-		"EAN": "EAN13",
-		"EAN-12": "EAN",
-		"EAN-8": "EAN8",
 		"ISBN-10": "ISBN10",
 		"ISBN-13": "ISBN13",
 	}
+
 	if erpnext_number in convert:
 		return convert[erpnext_number]
-	else:
-		return erpnext_number
+
+	return erpnext_number
 
 
 def make_item_price(item, price_list_name, item_price):
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index 67ed90d..0c6dc77 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -581,8 +581,9 @@
 			},
 			{"barcode": "72527273070", "barcode_type": "UPC-A"},
 			{"barcode": "123456", "barcode_type": "CODE-39"},
-			{"barcode": "401268452363", "barcode_type": "EAN-12"},
-			{"barcode": "90311017", "barcode_type": "EAN-8"},
+			{"barcode": "401268452363", "barcode_type": "EAN"},
+			{"barcode": "90311017", "barcode_type": "EAN"},
+			{"barcode": "73513537", "barcode_type": "EAN"},
 			{"barcode": "0123456789012", "barcode_type": "GS1"},
 			{"barcode": "2211564566668", "barcode_type": "GTIN"},
 			{"barcode": "0256480249", "barcode_type": "ISBN"},