Merge branch 'v4.x.x'
diff --git a/erpnext/__version__.py b/erpnext/__version__.py
index 61e02d0..2971b7f 100644
--- a/erpnext/__version__.py
+++ b/erpnext/__version__.py
@@ -1,2 +1,2 @@
 from __future__ import unicode_literals
-__version__ = '4.25.2'
+__version__ = '4.25.3'
diff --git a/erpnext/accounts/page/accounts_browser/accounts_browser.py b/erpnext/accounts/page/accounts_browser/accounts_browser.py
index 15cfdd2..2318588 100644
--- a/erpnext/accounts/page/accounts_browser/accounts_browser.py
+++ b/erpnext/accounts/page/accounts_browser/accounts_browser.py
@@ -6,6 +6,7 @@
 import frappe.defaults
 from frappe.utils import flt
 from erpnext.accounts.utils import get_balance_on
+from erpnext.accounts.report.financial_statements import sort_root_accounts
 
 @frappe.whitelist()
 def get_companies():
@@ -21,12 +22,15 @@
 	# root
 	if args['parent'] in ("Accounts", "Cost Centers"):
 		acc = frappe.db.sql(""" select 
-			name as value, if(group_or_ledger='Group', 1, 0) as expandable
+			name as value, if(group_or_ledger='Group', 1, 0) as expandable, root_type, report_type
 			from `tab%s`
 			where ifnull(parent_%s,'') = ''
 			and `company` = %s	and docstatus<2 
 			order by name""" % (ctype, ctype.lower().replace(' ','_'), '%s'),
 				company, as_dict=1)
+				
+		if args["parent"]=="Accounts":
+			sort_root_accounts(acc)
 	else:	
 		# other
 		acc = frappe.db.sql("""select 
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index 4ae61ca..fc4ebec 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -186,7 +186,10 @@
 	filtered_accounts = []
 	def add_to_list(parent, level):
 		if level < depth:
-			for child in (parent_children_map.get(parent) or []):
+			children = parent_children_map.get(parent) or []
+			if parent == None:
+				sort_root_accounts(children)
+			for child in children:
 				child.indent = level
 				filtered_accounts.append(child)
 				add_to_list(child.name, level + 1)
@@ -202,6 +205,22 @@
 	add_to_list(None, 0)
 
 	return filtered_accounts, accounts_by_name
+	
+def sort_root_accounts(roots):
+	"""Sort root types as Asset, Liability, Equity, Income, Expense"""
+
+	def compare_roots(a, b):
+		if a.report_type != b.report_type and a.report_type == "Balance Sheet":
+			return -1
+		if a.root_type != b.root_type and a.root_type == "Asset":
+			return -1
+		if a.root_type == "Liability" and b.root_type == "Equity":
+			return -1
+		if a.root_type == "Income" and b.root_type == "Expense":
+			return -1
+		return 1
+
+	roots.sort(compare_roots)
 
 def get_gl_entries(company, from_date, to_date, root_lft, root_rgt, ignore_closing_entries=False):
 	"""Returns a dict like { "account": [gl entries], ... }"""
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py
index dedbef9..0f72536 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -123,7 +123,8 @@
 def prepare_data(accounts, filters, total_row):
 	show_zero_values = cint(filters.show_zero_values)
 	data = []
-	for i, d in enumerate(accounts):
+	accounts_with_zero_value = []
+	for d in accounts:
 		has_value = False
 		row = {
 			"account_name": d.account_name,
@@ -140,9 +141,14 @@
 			row[key] = d.get(key, 0.0)
 			if row[key]:
 				has_value = True
-
-		if has_value or show_zero_values:
+		
+		if show_zero_values:
 			data.append(row)
+		else:
+			if not has_value:
+				accounts_with_zero_value.append(d.name)
+			elif d.parent_account not in accounts_with_zero_value:
+				data.append(row)
 
 	data.extend([{},total_row])
 
diff --git a/erpnext/buying/doctype/purchase_common/purchase_common.py b/erpnext/buying/doctype/purchase_common/purchase_common.py
index 2cf8673..25f429a 100644
--- a/erpnext/buying/doctype/purchase_common/purchase_common.py
+++ b/erpnext/buying/doctype/purchase_common/purchase_common.py
@@ -3,7 +3,7 @@
 
 from __future__ import unicode_literals
 import frappe
-from frappe.utils import flt
+from frappe.utils import flt, cstr
 from frappe import _
 
 from erpnext.stock.doctype.item.item import get_last_purchase_details
@@ -64,7 +64,7 @@
 							= d.rate = item_last_purchase_rate
 
 	def validate_for_items(self, obj):
-		check_list, chk_dupl_itm=[],[]
+		items = []
 		for d in obj.get(obj.fname):
 			# validation for valid qty
 			if flt(d.qty) < 0 or (d.parenttype != 'Purchase Receipt' and not flt(d.qty)):
@@ -96,30 +96,10 @@
 				if item[0][1] != 'Yes' and item[0][2] != 'Yes':
 					frappe.throw(_("{0} must be a Purchased or Sub-Contracted Item in row {1}").format(d.item_code, d.idx))
 
-			# list criteria that should not repeat if item is stock item
-			e = [getattr(d, "schedule_date", None), d.item_code, d.description, d.warehouse, d.uom,
-				d.meta.get_field('prevdoc_docname') and d.prevdoc_docname or d.meta.get_field('sales_order_no') and d.sales_order_no or '',
-				d.meta.get_field('prevdoc_detail_docname') and d.prevdoc_detail_docname or '',
-				d.meta.get_field('batch_no') and d.batch_no or '']
-
-			# if is not stock item
-			f = [getattr(d, "schedule_date", None), d.item_code, d.description]
-
-			ch = frappe.db.sql("""select is_stock_item from `tabItem` where name = %s""", d.item_code)
-
-			if ch and ch[0][0] == 'Yes':
-				# check for same items
-				if e in check_list:
-					frappe.throw(_("Item {0} has been entered multiple times with same description or date or warehouse").format(d.item_code))
-				else:
-					check_list.append(e)
-
-			elif ch and ch[0][0] == 'No':
-				# check for same items
-				if f in chk_dupl_itm:
-					frappe.throw(_("Item {0} has been entered multiple times with same description or date").format(d.item_code))
-				else:
-					chk_dupl_itm.append(f)
+			items.append(cstr(d.item_code))
+			if items and len(items) != len(set(items)):
+				frappe.msgprint(_("Warning: Same item has been entered multiple times."))
+			
 
 	def check_for_stopped_status(self, doctype, docname):
 		stopped = frappe.db.sql("""select name from `tab%s` where name = %s and
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 57916db..d1ac221 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 = "4.25.2"
+app_version = "4.25.3"
 
 error_report_email = "support@erpnext.com"
 
diff --git a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py
index f0bb937..efc4399 100644
--- a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py
+++ b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py
@@ -254,8 +254,10 @@
 					ifnull(sum(ifnull(fb.qty, 0)/ifnull(bom.quantity, 1)), 0) as qty,
 					fb.description, fb.stock_uom, it.min_order_qty
 					from `tabBOM Explosion Item` fb, `tabBOM` bom, `tabItem` it
-					where bom.name = fb.parent and it.name = fb.item_code and ifnull(it.is_pro_applicable, 'No') = 'No'
+					where bom.name = fb.parent and it.name = fb.item_code 
+					and ifnull(it.is_pro_applicable, 'No') = 'No'
 					and ifnull(it.is_sub_contracted_item, 'No') = 'No'
+					and ifnull(it.is_stock_item, 'No') = 'Yes'
 					and fb.docstatus<2 and bom.name=%s
 					group by item_code, stock_uom""", bom, as_dict=1):
 						bom_wise_item_details.setdefault(d.item_code, d)
@@ -268,6 +270,7 @@
 					from `tabBOM Item` bom_item, `tabBOM` bom, tabItem item
 					where bom.name = bom_item.parent and bom.name = %s and bom_item.docstatus < 2
 					and bom_item.item_code = item.name
+					and ifnull(item.is_stock_item, 'No') = 'Yes'
 					group by item_code""", bom, as_dict=1):
 						bom_wise_item_details.setdefault(d.item_code, d)
 
diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
index 3679457..aa5c194 100644
--- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
+++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
@@ -9,19 +9,20 @@
 def execute(filters=None):
 	if not filters: filters = {}
 
+	float_precision = cint(frappe.db.get_default("float_precision")) or 3
+	
 	columns = get_columns(filters)
 	item_map = get_item_details(filters)
-	iwb_map = get_item_warehouse_batch_map(filters)
+	iwb_map = get_item_warehouse_batch_map(filters, float_precision)
 
 	data = []
 	for item in sorted(iwb_map):
 		for wh in sorted(iwb_map[item]):
 			for batch in sorted(iwb_map[item][wh]):
 				qty_dict = iwb_map[item][wh][batch]
-				data.append([item, item_map[item]["item_name"],
-					item_map[item]["description"], wh, batch,
-					qty_dict.opening_qty, qty_dict.in_qty,
-					qty_dict.out_qty, qty_dict.bal_qty
+				data.append([item, item_map[item]["item_name"], item_map[item]["description"], wh, batch,
+					flt(qty_dict.opening_qty, float_precision), flt(qty_dict.in_qty, float_precision),
+					flt(qty_dict.out_qty, float_precision), flt(qty_dict.bal_qty, float_precision)
 				])
 
 	return columns, data
@@ -56,8 +57,7 @@
 		where docstatus < 2 %s order by item_code, warehouse""" %
 		conditions, as_dict=1)
 
-def get_item_warehouse_batch_map(filters):
-	float_precision = cint(frappe.db.get_default("float_precision")) or 3
+def get_item_warehouse_batch_map(filters, float_precision):
 	sle = get_stock_ledger_entries(filters)
 	iwb_map = {}
 
diff --git a/setup.py b/setup.py
index f628662..a103bed 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,7 @@
 from setuptools import setup, find_packages
 import os
 
-version = "4.25.2"
+version = "4.25.3"
 
 with open("requirements.txt", "r") as f:
 	install_requires = f.readlines()