diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js
index 0248fab4602..dd7e1d29e71 100644
--- a/erpnext/accounts/doctype/account/account_tree.js
+++ b/erpnext/accounts/doctype/account/account_tree.js
@@ -52,6 +52,47 @@ frappe.treeview_settings["Account"] = {
],
root_label: "Accounts",
get_tree_nodes: "erpnext.accounts.utils.get_children",
+ get_label: function (node) {
+ // clean display name — the account number renders as a badge (see
+ // onrender) instead of being glued into the name
+ return frappe.utils.escape_html(node.data.account_name || node.title || node.label);
+ },
+ onrender: function (node) {
+ if (node.is_root || !node.data) return;
+
+ const flags = [];
+ if (node.data.account_number) {
+ flags.push(frappe.ui.badge({ label: node.data.account_number, size: "sm" }));
+ }
+
+ const company = frappe.treeview_settings["Account"].treeview?.page?.fields_dict?.company?.get_value();
+ const company_currency = company && erpnext.get_currency(company);
+ if (
+ node.data.account_currency &&
+ company_currency &&
+ node.data.account_currency !== company_currency
+ ) {
+ flags.push(frappe.ui.badge({ label: node.data.account_currency, theme: "blue", size: "sm" }));
+ }
+
+ if (node.data.freeze_account === "Yes") {
+ flags.push(
+ $(
+ `
+ ${frappe.utils.icon("lock", "sm")}
+ `
+ )[0]
+ );
+ }
+
+ if (flags.length) {
+ const $flags = $(
+ ''
+ );
+ flags.forEach((flag) => $flags.append(flag));
+ $flags.insertAfter(node.$tree_link.find("a.tree-label"));
+ }
+ },
on_node_render: function (node, deep) {
const render_balances = () => {
for (let account of cur_tree.account_balance_data) {
@@ -232,7 +273,7 @@ frappe.treeview_settings["Account"] = {
frappe.treeview_settings["Account"].treeview["tree"] = treeview.tree;
if (treeview.can_create) {
treeview.page.set_primary_action(
- __("New"),
+ { label: __("Add Account"), short_label: __("Add") },
function () {
let root_company = treeview.page.fields_dict.root_company.get_value();
if (root_company) {
@@ -243,13 +284,14 @@ frappe.treeview_settings["Account"] = {
treeview.new_node();
}
},
- "add"
+ "plus"
);
}
},
toolbar: [
{
label: __("Add Child"),
+ icon: "plus",
condition: function (node) {
return (
frappe.boot.user.can_create.indexOf("Account") !== -1 &&
@@ -272,6 +314,7 @@ frappe.treeview_settings["Account"] = {
return !node.root && frappe.boot.user.can_read.indexOf("GL Entry") !== -1;
},
label: __("View Ledger"),
+ icon: "book-open",
click: function (node, btn) {
frappe.route_options = {
from_date: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
@@ -286,6 +329,106 @@ frappe.treeview_settings["Account"] = {
},
btnClass: "hidden-xs",
},
+ {
+ // same label and mechanism as the Account form's Actions button:
+ // NOT frappe's generic rename (Allow Rename stays off) — this is
+ // ERPNext's controlled update that rebuilds the derived
+ // "number - name - abbr" document name
+ label: __("Update Account Name / Number"),
+ icon: "text-cursor-input",
+ condition: function (node) {
+ return !node.is_root && frappe.model.can_write("Account");
+ },
+ click: function (node) {
+ const dialog = new frappe.ui.Dialog({
+ title: __("Update Account Number / Name"),
+ fields: [
+ {
+ fieldtype: "Data",
+ fieldname: "account_name",
+ label: __("Account Name"),
+ reqd: 1,
+ default: node.data.account_name,
+ },
+ {
+ fieldtype: "Data",
+ fieldname: "account_number",
+ label: __("Account Number"),
+ default: node.data.account_number,
+ },
+ ],
+ primary_action_label: __("Update"),
+ primary_action(values) {
+ dialog.hide();
+ frappe.dom.freeze(__("Updating {0}", [node.label]));
+ frappe.call({
+ method: "erpnext.accounts.doctype.account.account.update_account_number",
+ args: {
+ name: node.label,
+ account_name: values.account_name,
+ account_number: values.account_number,
+ },
+ callback: function (r) {
+ if (r.exc) return;
+ const treeview = frappe.views.trees["Account"];
+ node.parent_node && treeview.tree.load_children(node.parent_node);
+ },
+ always: function () {
+ frappe.dom.unfreeze();
+ },
+ });
+ },
+ });
+ dialog.show();
+ },
+ },
+ {
+ label: __("Convert to Group"),
+ icon: "folder-tree",
+ condition: function (node) {
+ return !node.is_root && !node.expandable && frappe.model.can_write("Account");
+ },
+ click: function (node) {
+ erpnext.accounts.convert_tree_node("Account", node, "convert_ledger_to_group");
+ },
+ },
+ {
+ label: __("Convert to Non-Group"),
+ icon: "file-text",
+ condition: function (node) {
+ // only on groups the user has opened and found empty — a
+ // group with children can't convert, so don't offer it
+ return (
+ !node.is_root &&
+ node.expandable &&
+ node.loaded &&
+ !node.$ul.children().length &&
+ frappe.model.can_write("Account")
+ );
+ },
+ click: function (node) {
+ erpnext.accounts.convert_tree_node("Account", node, "convert_group_to_ledger");
+ },
+ },
],
extend_toolbar: true,
};
+
+frappe.provide("erpnext.accounts");
+// shared by the Account and Cost Center tree views (defined in both files,
+// whichever loads first wins): run the doctype's whitelisted convert method,
+// then re-render the branch so the node's group/leaf state updates
+erpnext.accounts.convert_tree_node =
+ erpnext.accounts.convert_tree_node ||
+ function (doctype, node, method) {
+ frappe.call({
+ method: "run_doc_method",
+ args: { dt: doctype, dn: node.label, method: method },
+ callback: function (r) {
+ if (r.exc) return;
+ const treeview = frappe.views.trees[doctype];
+ node.parent_node && treeview.tree.load_children(node.parent_node);
+ frappe.show_alert({ message: __("{0} converted", [node.label]), indicator: "green" });
+ },
+ });
+ };
diff --git a/erpnext/accounts/doctype/cost_center/cost_center_tree.js b/erpnext/accounts/doctype/cost_center/cost_center_tree.js
index 3edeb8efb0b..5b134f6545f 100644
--- a/erpnext/accounts/doctype/cost_center/cost_center_tree.js
+++ b/erpnext/accounts/doctype/cost_center/cost_center_tree.js
@@ -42,6 +42,37 @@ frappe.treeview_settings["Cost Center"] = {
},
],
ignore_fields: ["parent_cost_center"],
+ toolbar: [
+ {
+ label: __("Convert to Group"),
+ icon: "folder-tree",
+ condition: function (node) {
+ return !node.is_root && !node.expandable && frappe.model.can_write("Cost Center");
+ },
+ click: function (node) {
+ erpnext.accounts.convert_tree_node("Cost Center", node, "convert_ledger_to_group");
+ },
+ },
+ {
+ label: __("Convert to Non-Group"),
+ icon: "file-text",
+ condition: function (node) {
+ // only on groups the user has opened and found empty — a
+ // group with children can't convert, so don't offer it
+ return (
+ !node.is_root &&
+ node.expandable &&
+ node.loaded &&
+ !node.$ul.children().length &&
+ frappe.model.can_write("Cost Center")
+ );
+ },
+ click: function (node) {
+ erpnext.accounts.convert_tree_node("Cost Center", node, "convert_group_to_ledger");
+ },
+ },
+ ],
+ extend_toolbar: true,
onload: function (treeview) {
function get_company() {
return treeview.page.fields_dict.company.get_value();
@@ -82,3 +113,22 @@ frappe.treeview_settings["Cost Center"] = {
);
},
};
+
+frappe.provide("erpnext.accounts");
+// shared by the Account and Cost Center tree views (defined in both files,
+// whichever loads first wins): run the doctype's whitelisted convert method,
+// then re-render the branch so the node's group/leaf state updates
+erpnext.accounts.convert_tree_node =
+ erpnext.accounts.convert_tree_node ||
+ function (doctype, node, method) {
+ frappe.call({
+ method: "run_doc_method",
+ args: { dt: doctype, dn: node.label, method: method },
+ callback: function (r) {
+ if (r.exc) return;
+ const treeview = frappe.views.trees[doctype];
+ node.parent_node && treeview.tree.load_children(node.parent_node);
+ frappe.show_alert({ message: __("{0} converted", [node.label]), indicator: "green" });
+ },
+ });
+ };
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index c2ce8ac39f9..db7a58c08c3 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -1367,12 +1367,13 @@ def get_children(
else:
filters.append([parent_fieldname, "=", parent])
+ account_fields = ["account_name", "account_number", "account_currency", "freeze_account"]
if is_root:
- fields += ["root_type", "report_type", "account_currency"] if doctype == "Account" else []
+ fields += ["root_type", "report_type", *account_fields] if doctype == "Account" else []
filters.append(["company", "=", company])
else:
- fields += ["root_type", "account_currency"] if doctype == "Account" else []
+ fields += ["root_type", *account_fields] if doctype == "Account" else []
fields += [parent_fieldname + " as parent"]
acc = frappe.get_list(doctype, fields=fields, filters=filters)
diff --git a/erpnext/manufacturing/doctype/bom/bom_tree.js b/erpnext/manufacturing/doctype/bom/bom_tree.js
index c4ecb853d7b..35a370e9baf 100644
--- a/erpnext/manufacturing/doctype/bom/bom_tree.js
+++ b/erpnext/manufacturing/doctype/bom/bom_tree.js
@@ -49,6 +49,8 @@ frappe.treeview_settings["BOM"] = {
{ toggle_btn: true },
{
label: __("Edit"),
+ icon: "pencil",
+ inline: true,
condition: function (node) {
return node.expandable;
},
diff --git a/erpnext/projects/doctype/task/task_tree.js b/erpnext/projects/doctype/task/task_tree.js
index fba1b309260..85d7db7e39b 100644
--- a/erpnext/projects/doctype/task/task_tree.js
+++ b/erpnext/projects/doctype/task/task_tree.js
@@ -40,6 +40,7 @@ frappe.treeview_settings["Task"] = {
toolbar: [
{
label: __("Add Multiple"),
+ icon: "list-plus",
condition: function (node) {
return node.expandable;
},
diff --git a/erpnext/setup/doctype/employee/employee_tree.js b/erpnext/setup/doctype/employee/employee_tree.js
index 26a89719d7f..81d0cd8a94d 100644
--- a/erpnext/setup/doctype/employee/employee_tree.js
+++ b/erpnext/setup/doctype/employee/employee_tree.js
@@ -16,6 +16,8 @@ frappe.treeview_settings["Employee"] = {
{ toggle_btn: true },
{
label: __("Edit"),
+ icon: "pencil",
+ inline: true,
condition: function (node) {
return !node.is_root;
},