perf: fetching of account balance in chart of accounts (#27661)

diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js
index 7516134..a4b6e0b 100644
--- a/erpnext/accounts/doctype/account/account_tree.js
+++ b/erpnext/accounts/doctype/account/account_tree.js
@@ -45,6 +45,49 @@
 	],
 	root_label: "Accounts",
 	get_tree_nodes: 'erpnext.accounts.utils.get_children',
+	on_get_node: function(nodes, deep=false) {
+		if (frappe.boot.user.can_read.indexOf("GL Entry") == -1) return;
+
+		let accounts = [];
+		if (deep) {
+			// in case of `get_all_nodes`
+			accounts = nodes.reduce((acc, node) => [...acc, ...node.data], []);
+		} else {
+			accounts = nodes;
+		}
+
+		const get_balances = frappe.call({
+			method: 'erpnext.accounts.utils.get_account_balances',
+			args: {
+				accounts: accounts,
+				company: cur_tree.args.company
+			},
+		});
+
+		get_balances.then(r => {
+			if (!r.message || r.message.length == 0) return;
+
+			for (let account of r.message) {
+
+				const node = cur_tree.nodes && cur_tree.nodes[account.value];
+				if (!node || node.is_root) continue;
+
+				// show Dr if positive since balance is calculated as debit - credit else show Cr
+				const balance = account.balance_in_account_currency || account.balance;
+				const dr_or_cr = balance > 0 ? "Dr": "Cr";
+				const format = (value, currency) => format_currency(Math.abs(value), currency);
+
+				if (account.balance!==undefined) {
+					$('<span class="balance-area pull-right">'
+						+ (account.balance_in_account_currency ?
+							(format(account.balance_in_account_currency, account.account_currency) + " / ") : "")
+						+ format(account.balance, account.company_currency)
+						+ " " + dr_or_cr
+						+ '</span>').insertBefore(node.$ul);
+				}
+			}
+		});
+	},
 	add_tree_node: 'erpnext.accounts.utils.add_ac',
 	menu_items:[
 		{
@@ -122,24 +165,6 @@
 			}
 		}, "add");
 	},
-	onrender: function(node) {
-		if (frappe.boot.user.can_read.indexOf("GL Entry") !== -1) {
-
-			// show Dr if positive since balance is calculated as debit - credit else show Cr
-			let balance = node.data.balance_in_account_currency || node.data.balance;
-			let dr_or_cr = balance > 0 ? "Dr": "Cr";
-
-			if (node.data && node.data.balance!==undefined) {
-				$('<span class="balance-area pull-right">'
-					+ (node.data.balance_in_account_currency ?
-						(format_currency(Math.abs(node.data.balance_in_account_currency),
-							node.data.account_currency) + " / ") : "")
-					+ format_currency(Math.abs(node.data.balance), node.data.company_currency)
-					+ " " + dr_or_cr
-					+ '</span>').insertBefore(node.$ul);
-			}
-		}
-	},
 	toolbar: [
 		{
 			label:__("Add Child"),
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index fbad171..fdd8d09 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -4,11 +4,14 @@
 
 from __future__ import unicode_literals
 
+from json import loads
+
 import frappe
 import frappe.defaults
 from frappe import _, throw
 from frappe.model.meta import get_field_precision
 from frappe.utils import cint, cstr, flt, formatdate, get_number_format_info, getdate, now, nowdate
+from six import string_types
 
 import erpnext
 
@@ -787,16 +790,28 @@
 
 	if doctype == 'Account':
 		sort_accounts(acc, is_root, key="value")
-		company_currency = frappe.get_cached_value('Company',  company,  "default_currency")
-		for each in acc:
-			each["company_currency"] = company_currency
-			each["balance"] = flt(get_balance_on(each.get("value"), in_account_currency=False, company=company))
-
-			if each.account_currency != company_currency:
-				each["balance_in_account_currency"] = flt(get_balance_on(each.get("value"), company=company))
 
 	return acc
 
+@frappe.whitelist()
+def get_account_balances(accounts, company):
+
+	if isinstance(accounts, string_types):
+		accounts = loads(accounts)
+
+	if not accounts:
+		return []
+
+	company_currency = frappe.get_cached_value("Company",  company,  "default_currency")
+
+	for account in accounts:
+		account["company_currency"] = company_currency
+		account["balance"] = flt(get_balance_on(account["value"], in_account_currency=False, company=company))
+		if account["account_currency"] and account["account_currency"] != company_currency:
+			account["balance_in_account_currency"] = flt(get_balance_on(account["value"], company=company))
+
+	return accounts
+
 def create_payment_gateway_account(gateway, payment_channel="Email"):
 	from erpnext.setup.setup_wizard.operations.install_fixtures import create_bank_account