feat: Added unit testing for Item Barcodes in item.py

Adds three different barcodes and barcodes types to a test item and
checks that they are added correctly.
Adds a barcode that already exists, and checks a DuplicateEntryError
is raised.
Adds an invalid EAN barcode and checks InvalidBarcode (a subclass of
ValidationError) is raised.
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 294e26d..566b638 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -32,6 +32,10 @@
 	pass
 
 
+class InvalidBarcode(frappe.ValidationError):
+	pass
+
+
 class Item(WebsiteGenerator):
 	website = frappe._dict(
 		page_title_field="item_name",
@@ -508,13 +512,13 @@
 						"""select parent from `tabItem Barcode` where barcode = %s and parent != %s""", (item_barcode.barcode, self.name))
 					if duplicate:
 						frappe.throw(_("Barcode {0} already used in Item {1}").format(
-							item_barcode.barcode, duplicate[0][0]))
+							item_barcode.barcode, duplicate[0][0]), frappe.DuplicateEntryError)
 
 					item_barcode.barcode_type = "" if item_barcode.barcode_type not in options else item_barcode.barcode_type
 					if item_barcode.barcode_type and item_barcode.barcode_type.upper() in ('EAN', 'UPC-A', 'EAN-13', 'EAN-8'):
 						if not ean.is_valid(item_barcode.barcode):
 							frappe.throw(_("Barcode {0} is not a valid {1} code").format(
-								item_barcode.barcode, item_barcode.barcode_type))
+								item_barcode.barcode, item_barcode.barcode_type), InvalidBarcode)
 
 	def validate_warehouse_for_reorder(self):
 		'''Validate Reorder level table for duplicate and conditional mandatory'''
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index 7ef4f8c..24292f7 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -8,7 +8,7 @@
 from frappe.test_runner import make_test_objects
 from erpnext.controllers.item_variant import (create_variant, ItemVariantExistsError,
 	InvalidItemAttributeValueError, get_variant)
-from erpnext.stock.doctype.item.item import StockExistsForTemplate
+from erpnext.stock.doctype.item.item import StockExistsForTemplate, InvalidBarcode
 from erpnext.stock.doctype.item.item import get_uom_conv_factor
 from frappe.model.rename_doc import rename_doc
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
@@ -305,6 +305,65 @@
 			item_doc.has_variants = 1
 			self.assertRaises(StockExistsForTemplate, item_doc.save)
 
+	def test_add_item_barcode(self):
+		# Clean up
+		frappe.db.sql("""delete from `tabItem Barcode`""")
+		item_code = "Test Item Barcode"
+		if frappe.db.exists("Item", item_code):
+			frappe.delete_doc("Item", item_code)
+
+		# Create new item and add barcodes
+		barcode_properties_list = [
+			{
+				"barcode": "0012345678905",
+				"barcode_type": "EAN"
+			},
+			{
+				"barcode": "012345678905",
+				"barcode_type": "UAN"
+			},
+			{
+				"barcode": "ARBITRARY_TEXT",
+			}
+		]
+		create_item(item_code)
+		for barcode_properties in barcode_properties_list:
+			item_doc = frappe.get_doc('Item', item_code)
+			new_barcode = item_doc.append('barcodes')
+			new_barcode.update(barcode_properties)
+			item_doc.save()
+
+		# Check values saved correctly
+		barcodes = frappe.get_list(
+			'Item Barcode',
+			fields=['barcode', 'barcode_type'],
+			filters={'parent': item_code})
+
+		for barcode_properties in barcode_properties_list:
+			barcode_to_find = barcode_properties['barcode']
+			matching_barcodes = [
+				x for x in barcodes
+				if x['barcode'] == barcode_to_find
+			]
+		self.assertEqual(len(matching_barcodes), 1)
+		details = matching_barcodes[0]
+
+		for key, value in iteritems(barcode_properties):
+			self.assertEqual(value, details.get(key))
+
+		# Add barcode again - should cause DuplicateEntryError
+		item_doc = frappe.get_doc('Item', item_code)
+		new_barcode = item_doc.append('barcodes')
+		new_barcode.update(barcode_properties_list[0])
+		self.assertRaises(frappe.DuplicateEntryError, item_doc.save)
+
+		# Add invalid barcode - should cause InvalidBarcode
+		item_doc = frappe.get_doc('Item', item_code)
+		new_barcode = item_doc.append('barcodes')
+		new_barcode.barcode = '9999999999999'
+		new_barcode.barcode_type = 'EAN'
+		self.assertRaises(InvalidBarcode, item_doc.save)
+
 def set_item_variant_settings(fields):
 	doc = frappe.get_doc('Item Variant Settings')
 	doc.set('fields', fields)