[enhancement] heatmaps on item and notifications for item
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index e4cba75..02d4244 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -324,3 +324,10 @@
 			frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
 			if not frozen_accounts_modifier in frappe.get_roles():
 				frappe.throw(_("{0} {1} is frozen").format(party_type, party_name), PartyFrozen)
+
+def get_timeline_data(doctype, name):
+	'''returns timeline data for the past one year'''
+	from frappe.desk.form.load import get_communication_data
+	data = get_communication_data(doctype, name, fields = 'unix_timestamp(date(creation)), count(name)',
+		group_by='group by date(creation)', as_dict=False)
+	return dict(data)
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py
index 4b384fa..9a3d58c 100644
--- a/erpnext/buying/doctype/supplier/supplier.py
+++ b/erpnext/buying/doctype/supplier/supplier.py
@@ -8,7 +8,7 @@
 from frappe.model.naming import make_autoname
 from erpnext.utilities.address_and_contact import load_address_and_contact
 from erpnext.utilities.transaction_base import TransactionBase
-from erpnext.accounts.party import validate_party_accounts
+from erpnext.accounts.party import validate_party_accounts, get_timeline_data
 from erpnext.accounts.party_status import get_party_status
 
 class Supplier(TransactionBase):
@@ -84,3 +84,14 @@
 		frappe.db.sql("""update `tabAddress` set address_title=%(newdn)s
 			{set_field} where supplier=%(newdn)s"""\
 			.format(set_field=set_field), ({"newdn": newdn}))
+
+@frappe.whitelist()
+def get_dashboard_data(name):
+	'''load dashboard related data'''
+	frappe.has_permission(doc=frappe.get_doc('Supplier', name), throw=True)
+
+	from frappe.desk.notifications import get_open_count
+	return {
+		'count': get_open_count('Supplier', name),
+		'timeline_data': get_timeline_data('Supplier', name),
+	}
diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py
index e769e3e..9e02baf 100755
--- a/erpnext/hr/doctype/employee/employee.py
+++ b/erpnext/hr/doctype/employee/employee.py
@@ -157,6 +157,28 @@
 	def on_trash(self):
 		delete_events(self.doctype, self.name)
 
+	def get_timeline_data(self):
+		'''returns timeline data based on attendance'''
+		return
+
+@frappe.whitelist()
+def get_dashboard_data(name):
+	'''load dashboard related data'''
+	frappe.has_permission(doc=frappe.get_doc('Employee', name), throw=True)
+
+	from frappe.desk.notifications import get_open_count
+	return {
+		'count': get_open_count('Employee', name),
+		'timeline_data': get_timeline_data(name),
+	}
+
+def get_timeline_data(name):
+	'''Return timeline for attendance'''
+	return dict(frappe.db.sql('''select unix_timestamp(att_date), count(*)
+		from `tabAttendance` where employee=%s
+			and att_date > date_sub(curdate(), interval 1 year)
+			and status in ('Present', 'Half Day')
+			group by att_date''', name))
 
 @frappe.whitelist()
 def get_retirement_date(date_of_birth=None):
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 975d589..84a4e19 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -261,3 +261,4 @@
 erpnext.patches.v6_20x.remove_customer_supplier_roles
 erpnext.patches.v6_24.rename_item_field
 erpnext.patches.v7_0.update_party_status
+erpnext.patches.v7_0.update_item_projected
diff --git a/erpnext/patches/v7_0/update_item_projected.py b/erpnext/patches/v7_0/update_item_projected.py
new file mode 100644
index 0000000..71b54af
--- /dev/null
+++ b/erpnext/patches/v7_0/update_item_projected.py
@@ -0,0 +1,7 @@
+import frappe
+
+def execute():
+	frappe.reload_doctype("Item")
+	from erpnext.stock.doctype.bin.bin import update_item_projected_qty
+	for item in frappe.get_all("Item", filters={"is_stock_item": 1}):
+		update_item_projected_qty(item.name)
\ No newline at end of file
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 51aedef..9902441 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -10,7 +10,7 @@
 from frappe.desk.reportview import build_match_conditions
 from erpnext.utilities.transaction_base import TransactionBase
 from erpnext.utilities.address_and_contact import load_address_and_contact
-from erpnext.accounts.party import validate_party_accounts
+from erpnext.accounts.party import validate_party_accounts, get_timeline_data
 from erpnext.accounts.party_status import get_party_status
 
 class Customer(TransactionBase):
@@ -128,6 +128,18 @@
 			{set_field} where customer=%(newdn)s"""\
 			.format(set_field=set_field), ({"newdn": newdn}))
 
+
+@frappe.whitelist()
+def get_dashboard_data(name):
+	'''load dashboard related data'''
+	frappe.has_permission(doc=frappe.get_doc('Customer', name), throw=True)
+
+	from frappe.desk.notifications import get_open_count
+	return {
+		'count': get_open_count('Customer', name),
+		'timeline_data': get_timeline_data('Customer', name),
+	}
+
 def get_customer_list(doctype, txt, searchfield, start, page_len, filters):
 	if frappe.db.get_default("cust_master_name") == "Customer Name":
 		fields = ["name", "customer_group", "territory"]
diff --git a/erpnext/startup/notifications.py b/erpnext/startup/notifications.py
index 700ced2..335efbb 100644
--- a/erpnext/startup/notifications.py
+++ b/erpnext/startup/notifications.py
@@ -10,6 +10,7 @@
 			"Warranty Claim": {"status": "Open"},
 			"Task": {"status": "Overdue"},
 			"Project": {"status": "Open"},
+			"Item": {"total_projected_qty": ("<", 0)},
 			"Customer": {"status": "Open"},
 			"Supplier": {"status": "Open"},
 			"Lead": {"status": "Open"},
@@ -40,7 +41,7 @@
 				"docstatus": ("<", 2)
 			},
 			"Purchase Receipt": {"docstatus": 0},
-			"Production Order": { "status": "In Process" },
+			"Production Order": { "status": ("in", ("Draft", "Not Started", "In Process")) },
 			"BOM": {"docstatus": 0},
 			"Timesheet": {"docstatus": 0},
 			"Time Log": {"status": "Draft"},
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index 1682686..3d57b4d 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -69,6 +69,7 @@
 		 	flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty)
 
 		self.save()
+		update_item_projected_qty(self.item_code)
 
 	def get_first_sle(self):
 		sle = frappe.db.sql("""
@@ -79,3 +80,9 @@
 			limit 1
 		""", (self.item_code, self.warehouse), as_dict=1)
 		return sle and sle[0] or None
+
+def update_item_projected_qty(item_code):
+	'''Set Item project qty'''
+	frappe.db.sql('''update tabItem set
+		total_projected_qty = (select sum(projected_qty) from tabBin where item_code=%s)
+		where name=%s''', (item_code, item_code))
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index 8a9d83c..900c40c 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -16,6 +16,12 @@
 
 	},
 
+	dashboard_update: function(frm) {
+		if(frm.dashboard_data.stock_data && frm.dashboard_data.stock_data.length) {
+			frm.dashboard.add_stats(frappe.render_template('item_dashboard', {data: frm.dashboard_data.stock_data}))
+		}
+	},
+
 	refresh: function(frm) {
 
 		if(frm.doc.is_stock_item) {
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index 94d2e8d..43fccf6 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -2285,6 +2285,31 @@
    "search_index": 0, 
    "set_only_once": 0, 
    "unique": 0
+  }, 
+  {
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "fieldname": "total_projected_qty", 
+   "fieldtype": "Float", 
+   "hidden": 1, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_list_view": 0, 
+   "label": "Total Projected Qty", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 1, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 1, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
   }
  ], 
  "hide_heading": 0, 
@@ -2298,7 +2323,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 1, 
- "modified": "2016-04-11 09:15:30.911215", 
+ "modified": "2016-04-14 07:51:07.058298", 
  "modified_by": "Administrator", 
  "module": "Stock", 
  "name": "Item", 
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 8286b5f..d43f68d 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -407,13 +407,13 @@
 					if self.check_if_linked_document_exists():
 						frappe.throw(_("As there are existing transactions for this item, \
 							you can not change the values of 'Has Serial No', 'Has Batch No', 'Is Stock Item' and 'Valuation Method'"))
-							
+
 	def check_if_linked_document_exists(self):
-		for doctype in ("Sales Order Item", "Delivery Note Item", "Sales Invoice Item", 
-			"Material Request Item", "Purchase Order Item", "Purchase Receipt Item", 
+		for doctype in ("Sales Order Item", "Delivery Note Item", "Sales Invoice Item",
+			"Material Request Item", "Purchase Order Item", "Purchase Receipt Item",
 			"Purchase Invoice Item", "Stock Entry Detail", "Stock Reconciliation Item"):
 			if frappe.db.get_value(doctype, filters={"item_code": self.name, "docstatus": 1}) or \
-				frappe.db.get_value("Production Order", 
+				frappe.db.get_value("Production Order",
 					filters={"production_item": self.name, "docstatus": 1}):
 				return True
 
@@ -583,6 +583,30 @@
 		if self.is_fixed_asset and self.is_stock_item:
 			frappe.throw(_("Fixed Asset Item must be a non-stock item"))
 
+
+@frappe.whitelist()
+def get_dashboard_data(name):
+	'''load dashboard related data'''
+	frappe.has_permission(doc=frappe.get_doc('Item', name), throw=True)
+
+	from frappe.desk.notifications import get_open_count
+	return {
+		'count': get_open_count('Item', name),
+		'timeline_data': get_timeline_data(name),
+		'stock_data': get_stock_data(name)
+	}
+
+def get_timeline_data(name):
+	'''returns timeline data based on stock ledger entry'''
+	return dict(frappe.db.sql('''select unix_timestamp(posting_date), count(*)
+		from `tabStock Ledger Entry` where item_code=%s
+			and posting_date > date_sub(curdate(), interval 1 year)
+			group by posting_date''', name))
+
+def get_stock_data(name):
+	return frappe.get_all('Bin', fields=['warehouse', 'actual_qty', 'projected_qty'],
+		filters={'item_code': name})
+
 def validate_end_of_life(item_code, end_of_life=None, disabled=None, verbose=1):
 	if (not end_of_life) or (disabled is None):
 		end_of_life, disabled = frappe.db.get_value("Item", item_code, ["end_of_life", "disabled"])
diff --git a/erpnext/stock/doctype/item/item_dashboard.html b/erpnext/stock/doctype/item/item_dashboard.html
new file mode 100644
index 0000000..a002a50
--- /dev/null
+++ b/erpnext/stock/doctype/item/item_dashboard.html
@@ -0,0 +1,12 @@
+<div style="padding-left: 15px;">
+	<h5>Stock Levels</h5>
+	<div class="row">
+		<div class="col-md-6 col-xs-12">
+			<ul class="list-unstyled">
+			{% data.every(function(d) { %}
+				<li class="small">{{ d.warehouse }}: {{ d.actual_qty }} ({{ d.projected_qty }})</li>
+			{% }) %}
+			</ul>
+		</div>
+	</div>
+</div>
\ No newline at end of file
diff --git a/erpnext/stock/doctype/item/item_links.py b/erpnext/stock/doctype/item/item_links.py
index c1fff8c..5586214 100644
--- a/erpnext/stock/doctype/item/item_links.py
+++ b/erpnext/stock/doctype/item/item_links.py
@@ -4,7 +4,8 @@
 	'fieldname': 'item_code',
 	'non_standard_fieldnames': {
 		'Production Order': 'production_item',
-		'Product Bundle': 'new_item_code'
+		'Product Bundle': 'new_item_code',
+		'Batch': 'item'
 	},
 	'transactions': [
 		{
diff --git a/erpnext/stock/doctype/item/item_list.js b/erpnext/stock/doctype/item/item_list.js
index fffc7fe..2fee589 100644
--- a/erpnext/stock/doctype/item/item_list.js
+++ b/erpnext/stock/doctype/item/item_list.js
@@ -1,10 +1,12 @@
 frappe.listview_settings['Item'] = {
 	add_fields: ["item_name", "stock_uom", "item_group", "image", "variant_of",
-		"has_variants", "end_of_life", "disabled", "is_sales_item"],
+		"has_variants", "end_of_life", "disabled", "total_projected_qty"],
 	filters: [["disabled", "=", "0"]],
 
 	get_indicator: function(doc) {
-		if (doc.disabled) {
+		if(doc.total_projected_qty < 0) {
+			return [__("Shortage"), "red", "total_projected_qty,<,0"];
+		} else if (doc.disabled) {
 			return [__("Disabled"), "grey", "disabled,=,Yes"];
 		} else if (doc.end_of_life && doc.end_of_life < frappe.datetime.get_today()) {
 			return [__("Expired"), "grey", "end_of_life,<,Today"];
@@ -12,8 +14,6 @@
 			return [__("Template"), "blue", "has_variants,=,Yes"];
 		} else if (doc.variant_of) {
 			return [__("Variant"), "green", "variant_of,=," + doc.variant_of];
-		} else {
-			return [__("Active"), "blue", "end_of_life,>=,Today"];
 		}
 	}
 };