Merge branch 'develop'
diff --git a/erpnext/__version__.py b/erpnext/__version__.py
index d886566..e6cafee 100644
--- a/erpnext/__version__.py
+++ b/erpnext/__version__.py
@@ -1,2 +1,2 @@
 from __future__ import unicode_literals
-__version__ = '5.0.27'
+__version__ = '5.0.28'
diff --git a/erpnext/change_log/v5/v5_0_28.md b/erpnext/change_log/v5/v5_0_28.md
new file mode 100644
index 0000000..6ad54e6
--- /dev/null
+++ b/erpnext/change_log/v5/v5_0_28.md
@@ -0,0 +1 @@
+- Open notification of Sales Order and Purchase Order based on whether Invoice is created against them. For eg. If a Sales Order is not Invoiced, it will be considered as open. Previously it was considered open if Delivery Note was created against Sales Order.
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 1caf7ca..ec57c80 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -5,7 +5,7 @@
 app_description = "Open Source Enterprise Resource Planning for Small and Midsized Organizations"
 app_icon = "icon-th"
 app_color = "#e74c3c"
-app_version = "5.0.27"
+app_version = "5.0.28"
 
 error_report_email = "support@erpnext.com"
 
diff --git a/erpnext/startup/notifications.py b/erpnext/startup/notifications.py
index e847cfa..4190f2d 100644
--- a/erpnext/startup/notifications.py
+++ b/erpnext/startup/notifications.py
@@ -14,7 +14,7 @@
 			"Contact": {"status": "Open"},
 			"Opportunity": {"status": "Open"},
 			"Quotation": {"docstatus": 0},
-			"Sales Order": { "per_delivered": ("<", 100), "status": ("!=", "Stopped"), "docstatus": ("<", 2) },
+			"Sales Order": { "per_billed": ("<", 100), "status": ("!=", "Stopped"), "docstatus": ("<", 2) },
 			"Journal Entry": {"docstatus": 0},
 			"Sales Invoice": { "outstanding_amount": (">", 0), "docstatus": ("<", 2) },
 			"Purchase Invoice": {"docstatus": 0},
@@ -25,7 +25,7 @@
 			"Delivery Note": {"docstatus": 0},
 			"Stock Entry": {"docstatus": 0},
 			"Material Request": {"docstatus": 0},
-			"Purchase Order": { "per_received": ("<", 100), "status": ("!=", "Stopped"), "docstatus": ("<", 2) },
+			"Purchase Order": { "per_billed": ("<", 100), "status": ("!=", "Stopped"), "docstatus": ("<", 2) },
 			"Production Order": { "status": "In Process" },
 			"BOM": {"docstatus": 0},
 			"Timesheet": {"docstatus": 0},
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index d823e7e..813a61b 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -5,7 +5,7 @@
 import frappe
 import frappe.defaults
 
-from frappe.utils import cstr, cint, flt, comma_or, get_datetime
+from frappe.utils import cstr, cint, flt, comma_or, get_datetime, getdate
 
 from frappe import _
 from erpnext.stock.utils import get_incoming_rate
@@ -66,6 +66,7 @@
 		self.validate_valuation_rate()
 		self.set_total_incoming_outgoing_value()
 		self.set_total_amount()
+		self.validate_batch()
 
 	def on_submit(self):
 		self.update_stock_ledger()
@@ -359,8 +360,11 @@
 		if self.purpose == "Subcontract" and self.purchase_order:
 			purchase_order = frappe.get_doc("Purchase Order", self.purchase_order)
 			for se_item in self.items:
-				total_allowed = [d.required_qty for d in purchase_order.supplied_items \
-					if d.rm_item_code == se_item.item_code][0]
+				total_allowed = sum([flt(d.required_qty) for d in purchase_order.supplied_items \
+					if d.rm_item_code == se_item.item_code])
+				if not total_allowed:
+					frappe.throw(_("Item {0} not found in 'Raw Materials Supplied' table in Purchase Order {1}")
+						.format(se_item.item_code, self.purchase_order))
 				total_supplied = frappe.db.sql("""select sum(qty)
 					from `tabStock Entry Detail`, `tabStock Entry`
 					where `tabStock Entry`.purchase_order = %s
@@ -721,6 +725,13 @@
 				mreq_item.warehouse != (item.s_warehouse if self.purpose== "Material Issue" else item.t_warehouse):
 					frappe.throw(_("Item or Warehouse for row {0} does not match Material Request").format(item.idx),
 						frappe.MappingMismatchError)
+						
+	def validate_batch(self):
+		if self.purpose == "Material Transfer for Manufacture":
+			for item in self.get("items"):
+				if item.batch_no:
+					if getdate(self.posting_date) > getdate(frappe.db.get_value("Batch", item.batch_no, "expiry_date")):
+						frappe.throw(_("Batch {0} of Item {1} has expired.").format(item.batch_no, item.item_code))
 
 @frappe.whitelist()
 def get_party_details(ref_dt, ref_dn):
diff --git a/setup.py b/setup.py
index 315b590..f30f3e7 100644
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,6 @@
 from setuptools import setup, find_packages
 
-version = "5.0.27"
+version = "5.0.28"
 
 with open("requirements.txt", "r") as f:
 	install_requires = f.readlines()