[Fix] Validate items for saleable, purchaseable or subcontractable in transactions (#14316)

* validate items to see if they are saleable

* check if items are subcontractable or saleable

* improvise name,error message and code
Validate type function validates item if they are of proper type for that transaction and throws descriptive error.
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 4b7b43c..3536233 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -35,6 +35,7 @@
 		if getattr(self, "supplier", None) and not self.supplier_name:
 			self.supplier_name = frappe.db.get_value("Supplier", self.supplier, "supplier_name")
 
+		self.validate_items()
 		self.set_qty_as_per_stock_uom()
 		self.validate_stock_or_nonstock_items()
 		self.validate_warehouse()
@@ -456,3 +457,26 @@
 		else:
 			frappe.throw(_("Please enter Reqd by Date"))
 
+	def validate_items(self):
+		# validate items to see if they have is_purchase_item or is_subcontracted_item enabled
+
+		if self.is_subcontracted:
+			validate_item_type(self, "is_sub_contracted_item", "subcontracted")
+		else:
+			validate_item_type(self, "is_purchase_item", "purchase")
+
+def validate_item_type(doc, fieldname, message):
+	# iterate through items and check if they are valid sales or purchase items
+	items = [d.item_code for d in doc.items]
+	item_list = ", ".join(["'%s'" % frappe.db.escape(d) for d in items])
+
+	invalid_items = [d[0] for d in frappe.db.sql("""
+		select item_code from tabItem where name in ({0}) and {1}=0
+		""".format(item_list, fieldname), as_list=True)]
+
+	if invalid_items:
+		frappe.throw(_("Following item {items} {verb} not marked as {message} item.\
+			You can enable them as {message} item from its Item master".format(
+				items = ", ".join([d for d in invalid_items]),
+				verb = "are" if len(invalid_items) > 1 else "is",
+				message = message)))
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 2a22b32..fcc9d75 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -35,6 +35,7 @@
 
 	def validate(self):
 		super(SellingController, self).validate()
+		self.validate_items()
 		self.validate_max_discount()
 		self.validate_selling_price()
 		self.set_qty_as_per_stock_uom()
@@ -337,6 +338,11 @@
 				po_nos = frappe.get_all('Sales Order', 'po_no', filters = {'name': ('in', sales_orders)})
 				self.po_no = ', '.join(list(set([d.po_no for d in po_nos if d.po_no])))
 
+	def validate_items(self):
+		# validate items to see if they have is_sales_item enabled
+		from erpnext.controllers.buying_controller import validate_item_type
+		validate_item_type(self, "is_sales_item", "sales")
+
 def check_active_sales_items(obj):
 	for d in obj.get("items"):
 		if d.item_code: