Merge branch 'schools' of https://github.com/neilLasrado/erpnext into neilLasrado-schools
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 20d39a8..5be0b69 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -35,25 +35,6 @@
 			'percent_join_field': 'purchase_order',
 			'overflow_type': 'billing'
 		}]
-		
-		self.prev_link_mapper = {
-			"Purchase Order": {
-				"fieldname": "purchase_order",
-				"doctype": "Purchase Invoice Item",
-				"filters": [
-					["Purchase Invoice Item", "parent", "=", self.name],
-					["Purchase Invoice Item", "purchase_order", "!=", ""]
-				]
-			},
-			"Purchase Receipt": {
-				"fieldname": "purchase_receipt",
-				"doctype": "Purchase Invoice Item",
-				"filters": [
-					["Purchase Invoice Item", "parent", "=", self.name],
-					["Purchase Invoice Item", "purchase_receipt", "!=", ""]
-				]
-			}
-		}
 
 	def validate(self):
 		if not self.is_opening:
@@ -65,7 +46,7 @@
 			self.po_required()
 			self.pr_required()
 			self.validate_supplier_invoice()
-			
+
 
 		# validate cash purchase
 		if (self.is_paid == 1):
@@ -96,7 +77,7 @@
 	def create_remarks(self):
 		if not self.remarks:
 			if self.bill_no and self.bill_date:
-				self.remarks = _("Against Supplier Invoice {0} dated {1}").format(self.bill_no, 
+				self.remarks = _("Against Supplier Invoice {0} dated {1}").format(self.bill_no,
 					formatdate(self.bill_date))
 			else:
 				self.remarks = _("No Remarks")
@@ -165,32 +146,32 @@
 				["Purchase Order", "purchase_order", "po_detail"],
 				["Purchase Receipt", "purchase_receipt", "pr_detail"]
 			])
-			
+
 	def set_expense_account(self):
 		auto_accounting_for_stock = cint(frappe.defaults.get_global_default("auto_accounting_for_stock"))
 
 		if auto_accounting_for_stock:
 			stock_not_billed_account = self.get_company_default("stock_received_but_not_billed")
 			stock_items = self.get_stock_items()
-			
+
 		if self.update_stock:
 			warehouse_account = get_warehouse_account()
-		
+
 		for item in self.get("items"):
 			# in case of auto inventory accounting,
-			# expense account is always "Stock Received But Not Billed" for a stock item 
+			# expense account is always "Stock Received But Not Billed" for a stock item
 			# except epening entry, drop-ship entry and fixed asset items
-			
+
 			if auto_accounting_for_stock and item.item_code in stock_items \
 				and self.is_opening == 'No' and not item.is_fixed_asset \
-				and (not item.po_detail or 
+				and (not item.po_detail or
 					not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")):
 
 				if self.update_stock:
 					item.expense_account = warehouse_account[item.warehouse]["name"]
 				else:
 					item.expense_account = stock_not_billed_account
-			
+
 			elif not item.expense_account:
 				throw(_("Expense account is mandatory for item {0}").format(item.item_code or item.item_name))
 
@@ -262,7 +243,7 @@
 					where name=`tabPurchase Invoice Item`.parent and update_stock=1 and is_return=1)"""
 			}
 		])
-	
+
 	def validate_purchase_receipt_if_update_stock(self):
 		if self.update_stock:
 			for item in self.get("items"):
@@ -273,30 +254,30 @@
 	def on_submit(self):
 		self.check_prev_docstatus()
 		self.update_status_updater_args()
-		
+
 		frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
 			self.company, self.base_grand_total)
-			
+
 		if not self.is_return:
 			self.update_against_document_in_jv()
 			self.update_prevdoc_status()
 			self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
 			self.update_billing_status_in_pr()
 
-		# Updating stock ledger should always be called after updating prevdoc status, 
+		# Updating stock ledger should always be called after updating prevdoc status,
 		# because updating ordered qty in bin depends upon updated ordered qty in PO
 		if self.update_stock == 1:
 			self.update_stock_ledger()
 			from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
 			update_serial_nos_after_submit(self, "items")
-			
+
 		# this sequence because outstanding may get -negative
 		self.make_gl_entries()
 
 		self.update_project()
 		self.update_fixed_asset()
-		
-	def update_fixed_asset(self):		
+
+	def update_fixed_asset(self):
 		for d in self.get("items"):
 			if d.is_fixed_asset:
 				asset = frappe.get_doc("Asset", d.asset)
@@ -307,10 +288,10 @@
 				else:
 					asset.purchase_invoice = None
 					asset.supplier = None
-					
+
 				asset.flags.ignore_validate_update_after_submit = True
 				asset.save()
-					
+
 	def make_gl_entries(self, repost_future_gle=False):
 		self.auto_accounting_for_stock = \
 			cint(frappe.defaults.get_global_default("auto_accounting_for_stock"))
@@ -319,24 +300,24 @@
 		self.expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
 		self.negative_expense_to_be_booked = 0.0
 		gl_entries = []
-		
-		
+
+
 		self.make_supplier_gl_entry(gl_entries)
 		self.make_item_gl_entries(gl_entries)
 		self.make_tax_gl_entries(gl_entries)
-		
+
 		gl_entries = merge_similar_entries(gl_entries)
-		
+
 		self.make_payment_gl_entries(gl_entries)
 
 		self.make_write_off_gl_entry(gl_entries)
-		
+
 		if gl_entries:
 			update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
-			
+
 			make_gl_entries(gl_entries,  cancel=(self.docstatus == 2),
 				update_outstanding=update_outstanding, merge_entries=False)
-			
+
 			if update_outstanding == "No":
 				update_outstanding_amt(self.credit_to, "Supplier", self.supplier,
 					self.doctype, self.return_against if cint(self.is_return) else self.name)
@@ -345,10 +326,10 @@
 				from erpnext.controllers.stock_controller import update_gl_entries_after
 				items, warehouses = self.get_items_and_warehouses()
 				update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items)
-					
+
 		elif self.docstatus == 2 and cint(self.update_stock) and self.auto_accounting_for_stock:
 			delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
-		
+
 
 	def make_supplier_gl_entry(self, gl_entries):
 		if self.grand_total:
@@ -374,18 +355,18 @@
 		stock_items = self.get_stock_items()
 		expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
 		warehouse_account = get_warehouse_account()
-		
+
 		for item in self.get("items"):
 			if flt(item.base_net_amount):
 				account_currency = get_account_currency(item.expense_account)
-				
+
 				if self.update_stock and self.auto_accounting_for_stock:
 					val_rate_db_precision = 6 if cint(item.precision("valuation_rate")) <= 6 else 9
 
 					# warehouse account
-					warehouse_debit_amount = flt(flt(item.valuation_rate, val_rate_db_precision) 
+					warehouse_debit_amount = flt(flt(item.valuation_rate, val_rate_db_precision)
 						* flt(item.qty)	* flt(item.conversion_factor), item.precision("base_net_amount"))
-						
+
 					gl_entries.append(
 						self.get_gl_dict({
 							"account": item.expense_account,
@@ -396,7 +377,7 @@
 							"project": item.project
 						}, account_currency)
 					)
-					
+
 					# Amount added through landed-cost-voucher
 					if flt(item.landed_cost_voucher_amount):
 						gl_entries.append(self.get_gl_dict({
@@ -424,14 +405,14 @@
 							"account": item.expense_account,
 							"against": self.supplier,
 							"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
-							"debit_in_account_currency": (flt(item.base_net_amount, 
-								item.precision("base_net_amount")) if account_currency==self.company_currency 
+							"debit_in_account_currency": (flt(item.base_net_amount,
+								item.precision("base_net_amount")) if account_currency==self.company_currency
 								else flt(item.net_amount, item.precision("net_amount"))),
 							"cost_center": item.cost_center,
 							"project": item.project
 						}, account_currency)
 					)
-				
+
 			if self.auto_accounting_for_stock and self.is_opening == "No" and \
 				item.item_code in stock_items and item.item_tax_amount:
 					# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
@@ -480,7 +461,7 @@
 				valuation_tax.setdefault(tax.cost_center, 0)
 				valuation_tax[tax.cost_center] += \
 					(tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.base_tax_amount_after_discount_amount)
-		
+
 		if self.is_opening == "No" and self.negative_expense_to_be_booked and valuation_tax:
 			# credit valuation tax amount in "Expenses Included In Valuation"
 			# this will balance out valuation amount included in cost of goods sold
@@ -525,7 +506,7 @@
 					"against_voucher_type": self.doctype,
 				}, self.party_account_currency)
 			)
-						
+
 			gl_entries.append(
 				self.get_gl_dict({
 					"account": self.cash_bank_account,
@@ -568,9 +549,9 @@
 
 	def on_cancel(self):
 		self.check_for_closed_status()
-		
+
 		self.update_status_updater_args()
-		
+
 		if not self.is_return:
 			from erpnext.accounts.utils import remove_against_link_from_jv
 			remove_against_link_from_jv(self.doctype, self.name)
@@ -579,11 +560,11 @@
 			self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
 			self.update_billing_status_in_pr()
 
-		# Updating stock ledger should always be called after updating prevdoc status, 
+		# Updating stock ledger should always be called after updating prevdoc status,
 		# because updating ordered qty in bin depends upon updated ordered qty in PO
 		if self.update_stock == 1:
 			self.update_stock_ledger()
-			
+
 		self.make_gl_entries_on_cancel()
 		self.update_project()
 		self.update_fixed_asset()
@@ -637,7 +618,7 @@
 
 		for pr in set(updated_pr):
 			frappe.get_doc("Purchase Receipt", pr).update_billing_percentage(update_modified=update_modified)
-			
+
 	def validate_fixed_asset_account(self):
 		for d in self.get('items'):
 			if d.is_fixed_asset:
@@ -658,12 +639,11 @@
 	if account:
 		if frappe.db.get_value("Account", account, "account_type") != "Fixed Asset":
 			account=None
-			
+
 	if not account:
 		asset_category, company = frappe.db.get_value("Asset", asset, ["asset_category", "company"])
-		
+
 		account = frappe.db.get_value("Asset Category Account",
 			filters={"parent": asset_category, "company_name": company}, fieldname="fixed_asset_account")
-			
+
 	return account
-			
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py
new file mode 100644
index 0000000..9b950db
--- /dev/null
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py
@@ -0,0 +1,31 @@
+from frappe import _
+
+data = {
+	'fieldname': 'purchase_invoice',
+	'non_standard_fieldnames': {
+		'Delivery Note': 'against_sales_invoice',
+		'Journal Entry': 'reference_name',
+		'Payment Entry': 'reference_name',
+		'Payment Request': 'reference_name',
+		'Landed Cost Voucher': 'receipt_document',
+		'Purchase Invoice': 'return_against'
+	},
+	'internal_links': {
+		'Purchase Order': ['items', 'sales_order'],
+		'Purchase Receipt': ['items', 'delivery_note'],
+	},
+	'transactions': [
+		{
+			'label': _('Payment'),
+			'items': ['Payment Entry', 'Payment Request', 'Journal Entry']
+		},
+		{
+			'label': _('Reference'),
+			'items': ['Purchase Order', 'Purchase Receipt', 'Asset', 'Landed Cost Voucher']
+		},
+		{
+			'label': _('Returns'),
+			'items': ['Purchase Invoice']
+		},
+	]
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index bb489e7..4ea18ac 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -50,9 +50,9 @@
 		pi = frappe.copy_doc(test_records[1])
 		pi.insert()
 		pi.submit()
-		
+
 		self.check_gle_for_pi(pi.name)
-		
+
 		set_perpetual_inventory(0)
 
 	def test_gl_entries_with_auto_accounting_for_stock_against_pr(self):
@@ -316,59 +316,59 @@
 			where voucher_type='Sales Invoice' and voucher_no=%s""", pi.name)
 
 		self.assertFalse(gle)
-	
+
 	def test_purchase_invoice_update_stock_gl_entry_with_perpetual_inventory(self):
 		set_perpetual_inventory()
-		
-		pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(), 
+
+		pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
 			posting_time=frappe.utils.nowtime())
-		
+
 		gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
 			debit_in_account_currency, credit_in_account_currency
 			from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
 			order by account asc""", pi.name, as_dict=1)
 
 		self.assertTrue(gl_entries)
-		
+
 		expected_gl_entries = dict((d[0], d) for d in [
 			[pi.credit_to, 0.0, 250.0],
 			[pi.items[0].warehouse, 250.0, 0.0]
 		])
-		
+
 		for i, gle in enumerate(gl_entries):
 			self.assertEquals(expected_gl_entries[gle.account][0], gle.account)
 			self.assertEquals(expected_gl_entries[gle.account][1], gle.debit)
 			self.assertEquals(expected_gl_entries[gle.account][2], gle.credit)
-			
+
 	def test_purchase_invoice_for_is_paid_and_update_stock_gl_entry_with_perpetual_inventory(self):
 		set_perpetual_inventory()
-		pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(), 
+		pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
 			posting_time=frappe.utils.nowtime(), cash_bank_account="Cash - _TC", is_paid=1)
 
-		gl_entries = frappe.db.sql("""select account, account_currency, sum(debit) as debit, 
-				sum(credit) as credit, debit_in_account_currency, credit_in_account_currency 
-			from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s 
+		gl_entries = frappe.db.sql("""select account, account_currency, sum(debit) as debit,
+				sum(credit) as credit, debit_in_account_currency, credit_in_account_currency
+			from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
 			group by account, voucher_no order by account asc;""", pi.name, as_dict=1)
 
 		self.assertTrue(gl_entries)
-		
+
 		expected_gl_entries = dict((d[0], d) for d in [
 			[pi.credit_to, 250.0, 250.0],
 			[pi.items[0].warehouse, 250.0, 0.0],
 			["Cash - _TC", 0.0, 250.0]
 		])
-				
+
 		for i, gle in enumerate(gl_entries):
 			self.assertEquals(expected_gl_entries[gle.account][0], gle.account)
 			self.assertEquals(expected_gl_entries[gle.account][1], gle.debit)
 			self.assertEquals(expected_gl_entries[gle.account][2], gle.credit)
-	
+
 	def test_update_stock_and_purchase_return(self):
 		actual_qty_0 = get_qty_after_transaction()
-		
+
 		pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
 			posting_time=frappe.utils.nowtime())
-		
+
 		actual_qty_1 = get_qty_after_transaction()
 		self.assertEquals(actual_qty_0 + 5, actual_qty_1)
 
@@ -377,37 +377,38 @@
 
 		actual_qty_2 = get_qty_after_transaction()
 		self.assertEquals(actual_qty_1 - 2, actual_qty_2)
-		
+
 		pi1.cancel()
 		self.assertEquals(actual_qty_1, get_qty_after_transaction())
-		
+
+		pi.reload()
 		pi.cancel()
 		self.assertEquals(actual_qty_0, get_qty_after_transaction())
-		
+
 	def test_subcontracting_via_purchase_invoice(self):
 		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
-		
+
 		make_stock_entry(item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, basic_rate=100)
-		make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse 1 - _TC", 
+		make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse 1 - _TC",
 			qty=100, basic_rate=100)
-		
-		pi = make_purchase_invoice(item_code="_Test FG Item", qty=10, rate=500, 
+
+		pi = make_purchase_invoice(item_code="_Test FG Item", qty=10, rate=500,
 			update_stock=1, is_subcontracted="Yes")
-		
+
 		self.assertEquals(len(pi.get("supplied_items")), 2)
-		
+
 		rm_supp_cost = sum([d.amount for d in pi.get("supplied_items")])
 		self.assertEquals(pi.get("items")[0].rm_supp_cost, flt(rm_supp_cost, 2))
-		
+
 	def test_rejected_serial_no(self):
 		pi = make_purchase_invoice(item_code="_Test Serialized Item With Series", received_qty=2, qty=1,
 			rejected_qty=1, rate=500, update_stock=1,
 			rejected_warehouse = "_Test Rejected Warehouse - _TC")
-		
+
 		self.assertEquals(frappe.db.get_value("Serial No", pi.get("items")[0].serial_no, "warehouse"),
 			pi.get("items")[0].warehouse)
-			
-		self.assertEquals(frappe.db.get_value("Serial No", pi.get("items")[0].rejected_serial_no, 
+
+		self.assertEquals(frappe.db.get_value("Serial No", pi.get("items")[0].rejected_serial_no,
 			"warehouse"), pi.get("items")[0].rejected_warehouse)
 
 def make_purchase_invoice(**args):
@@ -420,10 +421,10 @@
 		pi.update_stock = 1
 	if args.is_paid:
 		pi.is_paid = 1
-		
+
 	if args.cash_bank_account:
 		pi.cash_bank_account=args.cash_bank_account
-		
+
 	pi.company = args.company or "_Test Company"
 	pi.supplier = args.supplier or "_Test Supplier"
 	pi.currency = args.currency or "INR"
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 1b68a83..1df86aa 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -39,25 +39,6 @@
 			'keyword': 'Billed',
 			'overflow_type': 'billing'
 		}]
-		
-		self.prev_link_mapper = {
-			"Sales Order": {
-				"fieldname": "sales_order",
-				"doctype": "Sales Invoice Item",
-				"filters": [
-					["Sales Invoice Item", "parent", "=", self.name],
-					["Sales Invoice Item", "sales_order", "!=", ""]
-				]
-			},
-			"Delivery Note": {
-				"fieldname": "delivery_note",
-				"doctype": "Sales Invoice Item",
-				"filters": [
-					["Sales Invoice Item", "parent", "=", self.name],
-					["Sales Invoice Item", "delivery_note", "!=", ""]
-				]
-			}
-		}
 
 	def set_indicator(self):
 		"""Set indicator for portal"""
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py
new file mode 100644
index 0000000..f494303
--- /dev/null
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py
@@ -0,0 +1,30 @@
+from frappe import _
+
+data = {
+	'fieldname': 'sales_invoice',
+	'non_standard_fieldnames': {
+		'Delivery Note': 'against_sales_invoice',
+		'Journal Entry': 'reference_name',
+		'Payment Entry': 'reference_name',
+		'Payment Request': 'reference_name',
+		'Sales Invoice': 'return_against'
+	},
+	'internal_links': {
+		'Sales Order': ['items', 'sales_order'],
+		'Delivery Note': ['items', 'delivery_note'],
+	},
+	'transactions': [
+		{
+			'label': _('Payment'),
+			'items': ['Payment Entry', 'Payment Request', 'Journal Entry']
+		},
+		{
+			'label': _('Reference'),
+			'items': ['Timesheet', 'Delivery Note', 'Sales Order']
+		},
+		{
+			'label': _('Returns'),
+			'items': ['Sales Invoice']
+		},
+	]
+}
\ No newline at end of file
diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js
index bc07aca..af46dc0 100644
--- a/erpnext/accounts/page/pos/pos.js
+++ b/erpnext/accounts/page/pos/pos.js
@@ -683,7 +683,7 @@
 				me.validate()
 				me.create_invoice();
 				me.make_payment();
-			});
+			}, "octicon octicon-credit-card");
 		}else if(this.frm.doc.docstatus == 0 && this.frm.doc.items.length){
 			this.page.set_primary_action(__("Submit"), function() {
 				me.validate()
@@ -702,7 +702,7 @@
 		this.page.set_secondary_action(__("New"), function() {
 			me.save_previous_entry();
 			me.create_new();
-		}).addClass("btn-primary");
+		}, "octicon octicon-plus").addClass("btn-primary");
 	},
 
 	print_document: function(html){
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 3885736..79b22ae 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -32,17 +32,6 @@
 			'percent_join_field': 'material_request',
 			'overflow_type': 'order'
 		}]
-		
-		self.prev_link_mapper = {
-			"Supplier Quotation": {
-				"fieldname": "supplier_quotation",
-				"doctype": "Purchase Order Item",
-				"filters": [
-					["Purchase Order Item", "parent", "=", self.name],
-					["Purchase Order Item", "supplier_quotation", "!=", ""]
-				]
-			}
-		}
 
 	def validate(self):
 		super(PurchaseOrder, self).validate()
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py
new file mode 100644
index 0000000..6c8ae7c
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_dashboard.py
@@ -0,0 +1,20 @@
+from frappe import _
+
+data = {
+	'fieldname': 'supplier_quotation',
+	'internal_links': {
+		'Material Request': ['items', 'material_request'],
+		'Request for Quotation': ['items', 'request_for_quotation'],
+		'Project': ['items', 'project'],
+	},
+	'transactions': [
+		{
+			'label': _('Related'),
+			'items': ['Purchase Order']
+		},
+		{
+			'label': _('Reference'),
+			'items': ['Material Request', 'Request for Quotation', 'Project']
+		},
+	]
+}
diff --git a/erpnext/portal/doctype/homepage/homepage.py b/erpnext/portal/doctype/homepage/homepage.py
index a817230..20ce767 100644
--- a/erpnext/portal/doctype/homepage/homepage.py
+++ b/erpnext/portal/doctype/homepage/homepage.py
@@ -17,9 +17,10 @@
 		for d in frappe.get_all('Item', fields=['name', 'item_name', 'description', 'image'],
 			filters={'show_in_website': 1}, limit=3):
 
-			# set missing routes (?)
 			doc = frappe.get_doc('Item', d.name)
-			doc.save()
+			if not doc.route:
+				# set missing route
+				doc.save()
 			self.append('products', dict(item_code=d.name,
 				item_name=d.item_name, description=d.description, image=d.image))
 
diff --git a/erpnext/public/build.json b/erpnext/public/build.json
index cf412e1..340ebdb 100644
--- a/erpnext/public/build.json
+++ b/erpnext/public/build.json
@@ -26,8 +26,7 @@
 		"public/js/payment/pos_payment.html",
 		"public/js/payment/payment_details.html",
 		"public/js/templates/item_selector.html",
-		"public/js/utils/item_selector.js",
-		"public/js/utils/document_flow.js"
+		"public/js/utils/item_selector.js"
 	],
 	"js/item-dashboard.min.js": [
 		"stock/dashboard/item_dashboard.html",
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 0b6b0a6..ece8284 100644
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -33,7 +33,7 @@
 
 	setup_serial_no: function() {
 		var grid_row = cur_frm.open_grid_row();
-		if(!grid_row.fields_dict.serial_no ||
+		if(!grid_row || !grid_row.fields_dict.serial_no ||
 			grid_row.fields_dict.serial_no.get_status()!=="Write") return;
 
 		var $btn = $('<button class="btn btn-sm btn-default">'+__("Add Serial No")+'</button>')
diff --git a/erpnext/public/js/utils/document_flow.js b/erpnext/public/js/utils/document_flow.js
deleted file mode 100644
index 0bc0b44..0000000
--- a/erpnext/public/js/utils/document_flow.js
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-// MIT License. See license.txt
-
-// for module flow
-
-$.extend(frappe.document_flow, {
-	"Selling": {
-		"Sales Order": ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "Payment Entry"],
-		"Quotation": ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "Payment Entry"]
-	},
-	"Accounts": {
-		"Sales Invoice": ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "Payment Entry"],
-		"Purchase Invoice": ["Supplier Quotation", "Purchase Order", "Purchase Receipt",
-			"Purchase Invoice", "Payment Entry"]
-	},
-	"Buying": {
-		// "Purchase Order": ["Supplier Quotation", "Purchase Order", "Purchase Receipt",
-		// 	"Purchase Invoice", "Payment Entry"],
-		"Supplier Quotation": ["Supplier Quotation", "Purchase Order", "Purchase Receipt",
-			"Purchase Invoice", "Payment Entry"]
-	},
-	"Stock": {
-		"Delivery Note": ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "Payment Entry"],
-		"Purchase Receipt": ["Supplier Quotation", "Purchase Order", "Purchase Receipt",
-			"Purchase Invoice", "Payment Entry"]
-	}
-});
diff --git a/erpnext/selling/doctype/quotation/quotation_dashboard.py b/erpnext/selling/doctype/quotation/quotation_dashboard.py
new file mode 100644
index 0000000..80dc6aa
--- /dev/null
+++ b/erpnext/selling/doctype/quotation/quotation_dashboard.py
@@ -0,0 +1,11 @@
+from frappe import _
+
+data = {
+	'fieldname': 'prevdoc_docname',
+	'transactions': [
+		{
+			'label': _('Related'),
+			'items': ['Sales Order']
+		},
+	]
+}
\ No newline at end of file
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index e4a1aab..ced3679 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -24,17 +24,6 @@
 	def __init__(self, arg1, arg2=None):
 		super(SalesOrder, self).__init__(arg1, arg2)
 
-		self.prev_link_mapper = {
-			"Quotation": {
-				"fieldname": "prevdoc_docname",
-				"doctype": "Sales Order Item",
-				"filters": [
-					["Sales Order Item", "parent", "=", self.name],
-					["Sales Order Item", "prevdoc_docname", "!=", ""]
-				]
-			}
-		}
-
 	def validate(self):
 		super(SalesOrder, self).validate()
 
diff --git a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py
new file mode 100644
index 0000000..feda39d
--- /dev/null
+++ b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py
@@ -0,0 +1,33 @@
+from frappe import _
+
+data = {
+	'fieldname': 'sales_order',
+	'non_standard_fieldnames': {
+		'Delivery Note': 'against_sales_order',
+	},
+	'internal_links': {
+		'Quotation': ['items', 'prevdoc_docname']
+	},
+	'transactions': [
+		{
+			'label': _('Fulfillment'),
+			'items': ['Sales Invoice', 'Delivery Note']
+		},
+		{
+			'label': _('Purchasing'),
+			'items': ['Material Request', 'Purchase Order']
+		},
+		{
+			'label': _('Projects'),
+			'items': ['Project']
+		},
+		{
+			'label': _('Manufacturing'),
+			'items': ['Production Order']
+		},
+		{
+			'label': _('Reference'),
+			'items': ['Quotation']
+		},
+	]
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index d8defc0..5e31ac3 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -60,19 +60,7 @@
 			'source_field': '-1 * qty',
 			'extra_cond': """ and exists (select name from `tabDelivery Note` where name=`tabDelivery Note Item`.parent and is_return=1)"""
 		}]
-		
-		self.prev_link_mapper = {
-			"Sales Order": {
-				"fieldname": "against_sales_order",
-				"doctype": "Delivery Note Item",
-				"filters": [
-					["Delivery Note Item", "parent", "=", self.name],
-					["Delivery Note Item", "against_sales_order", "!=", ""]
-				]
-			}
-		}
 
-		
 	def before_print(self):
 		def toggle_print_hide(meta, fieldname):
 			df = meta.get_field(fieldname)
@@ -203,7 +191,7 @@
 		if not self.is_return:
 			self.check_credit_limit()
 
-		# Updating stock ledger should always be called after updating prevdoc status, 
+		# Updating stock ledger should always be called after updating prevdoc status,
 		# because updating reserved qty in bin depends upon updated delivered qty in SO
 		self.update_stock_ledger()
 		self.make_gl_entries()
@@ -215,7 +203,7 @@
 		self.update_prevdoc_status()
 		self.update_billing_status()
 
-		# Updating stock ledger should always be called after updating prevdoc status, 
+		# Updating stock ledger should always be called after updating prevdoc status,
 		# because updating reserved qty in bin depends upon updated delivered qty in SO
 		self.update_stock_ledger()
 
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py b/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py
new file mode 100644
index 0000000..c2b54f8
--- /dev/null
+++ b/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py
@@ -0,0 +1,26 @@
+from frappe import _
+
+data = {
+	'fieldname': 'delivery_note_no',
+	'non_standard_fieldnames': {
+		'Sales Invoice': 'delivery_note',
+		'Packing Slip': 'delivery_note',
+	},
+	'internal_links': {
+		'Sales Order': ['items', 'against_sales_order'],
+	},
+	'transactions': [
+		{
+			'label': _('Related'),
+			'items': ['Sales Invoice', 'Packing Slip']
+		},
+		{
+			'label': _('Reference'),
+			'items': ['Sales Order', 'Quality Inspection']
+		},
+		{
+			'label': _('Returns'),
+			'items': ['Stock Entry']
+		},
+	]
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 5faefde..258331c 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -44,17 +44,6 @@
 			# 'overflow_type': 'receipt',
 			'extra_cond': """ and exists (select name from `tabPurchase Receipt` where name=`tabPurchase Receipt Item`.parent and is_return=1)"""
 		}]
-		
-		self.prev_link_mapper = {
-			"Purchase Order": {
-				"fieldname": "purchase_order",
-				"doctype": "Purchase Receipt Item",
-				"filters": [
-					["Purchase Receipt Item", "parent", "=", self.name],
-					["Purchase Receipt Item", "purchase_order", "!=", ""]
-				]
-			}
-		}
 
 	def validate(self):
 		super(PurchaseReceipt, self).validate()
@@ -123,7 +112,7 @@
 		purchase_controller = frappe.get_doc("Purchase Common")
 
 		# Check for Approving Authority
-		frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, 
+		frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
 			self.company, self.base_grand_total)
 
 		# Set status as Submitted
@@ -135,7 +124,7 @@
 		if not self.is_return:
 			purchase_controller.update_last_purchase_rate(self, 1)
 
-		# Updating stock ledger should always be called after updating prevdoc status, 
+		# Updating stock ledger should always be called after updating prevdoc status,
 		# because updating ordered qty in bin depends upon updated ordered qty in PO
 		self.update_stock_ledger()
 
@@ -166,13 +155,13 @@
 
 		frappe.db.set(self,'status','Cancelled')
 
-		self.update_prevdoc_status()		
+		self.update_prevdoc_status()
 		self.update_billing_status()
 
 		if not self.is_return:
 			pc_obj.update_last_purchase_rate(self, 0)
-		
-		# Updating stock ledger should always be called after updating prevdoc status, 
+
+		# Updating stock ledger should always be called after updating prevdoc status,
 		# because updating ordered qty in bin depends upon updated ordered qty in PO
 		self.update_stock_ledger()
 		self.make_gl_entries_on_cancel()
@@ -199,8 +188,8 @@
 		for d in self.get("items"):
 			if d.item_code in stock_items and flt(d.valuation_rate) and flt(d.qty):
 				if warehouse_account.get(d.warehouse):
-					stock_value_diff = frappe.db.get_value("Stock Ledger Entry", 
-						{"voucher_type": "Purchase Receipt", "voucher_no": self.name, 
+					stock_value_diff = frappe.db.get_value("Stock Ledger Entry",
+						{"voucher_type": "Purchase Receipt", "voucher_no": self.name,
 						"voucher_detail_no": d.name}, "stock_value_difference")
 					if not stock_value_diff:
 						continue
@@ -251,15 +240,15 @@
 					valuation_amount_as_per_doc = flt(d.base_net_amount, d.precision("base_net_amount")) + \
 						flt(d.landed_cost_voucher_amount) + flt(d.rm_supp_cost) + flt(d.item_tax_amount)
 
-					divisional_loss = flt(valuation_amount_as_per_doc - stock_value_diff, 
+					divisional_loss = flt(valuation_amount_as_per_doc - stock_value_diff,
 						d.precision("base_net_amount"))
-						
+
 					if divisional_loss:
 						if self.is_return or flt(d.item_tax_amount):
 							loss_account = expenses_included_in_valuation
 						else:
 							loss_account = stock_rbnb
-							
+
 						gl_entries.append(self.get_gl_dict({
 							"account": loss_account,
 							"against": warehouse_account[d.warehouse]["name"],
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py
new file mode 100644
index 0000000..3278032
--- /dev/null
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py
@@ -0,0 +1,28 @@
+from frappe import _
+
+data = {
+	'fieldname': 'purchase_receipt_no',
+	'non_standard_fieldnames': {
+		'Purchase Invoice': 'purchase_receipt',
+		'Landed Cost Voucher': 'receipt_document'
+	},
+	'internal_links': {
+		'Purchase Order': ['items', 'purchase_order'],
+		'Project': ['items', 'project'],
+		'Quality Inspection': ['items', 'qa_no'],
+	},
+	'transactions': [
+		{
+			'label': _('Related'),
+			'items': ['Purchase Invoice', 'Landed Cost Voucher']
+		},
+		{
+			'label': _('Reference'),
+			'items': ['Purchase Order', 'Quality Inspection', 'Project']
+		},
+		{
+			'label': _('Returns'),
+			'items': ['Stock Entry']
+		},
+	]
+}
\ No newline at end of file
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 8d17a1e..fa6dd3c 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -346,9 +346,8 @@
 
 
 
-		if (frappe.local.flags.currently_saving
-			and frappe.local.flags.currently_saving.doctype==self.exceptions[0]["voucher_type"]
-			and frappe.local.flags.currently_saving.name==self.exceptions[0]["voucher_no"]):
+		if ((self.exceptions[0]["voucher_type"], self.exceptions[0]["voucher_no"]) in
+			frappe.local.flags.currently_saving):
 			msg = _("{0} units of {1} needed in {2} to complete this transaction.").format(
 				abs(deficiency), frappe.get_desk_link('Item', self.item_code),
 				frappe.get_desk_link('Warehouse', self.warehouse))