add Chart of Accounts export button to list and tree views

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ali 2026-03-26 15:24:00 +04:00
parent 989cce52b4
commit df9f72be11
4 changed files with 113 additions and 0 deletions

View File

@ -0,0 +1,68 @@
import re
import frappe
# 223.1223.5: IBAN (AZ + 2 digits + 4 uppercase letters + 20 digits = 28 chars)
_IBAN_RE = re.compile(r"AZ\d{2}[A-Z]{4}\d{20}")
# 223.6: card/account number — любая последовательность 10+ цифр подряд
_DIGITS_RE = re.compile(r"\d{10,}")
MASK_IBAN = {"223.1", "223.2", "223.3", "223.4", "223.5"}
MASK_CARD = {"223.6"}
def _mask(account_name, account_number):
if account_number in MASK_IBAN:
account_name = _IBAN_RE.sub(lambda m: "X" * len(m.group()), account_name)
if account_number in MASK_CARD:
account_name = _DIGITS_RE.sub(lambda m: "X" * len(m.group()), account_name)
return account_name
def _build_node(account, children_of):
node = {}
if account.account_number:
node["account_number"] = account.account_number
if account.root_type:
node["root_type"] = account.root_type
if account.is_group:
node["is_group"] = 1
if account.account_type:
node["account_type"] = account.account_type
for child in children_of.get(account.name, []):
key = _mask(child.account_name, child.account_number or "")
node[key] = _build_node(child, children_of)
return node
@frappe.whitelist()
def export_chart_of_accounts():
accounts = frappe.get_all(
"Account",
fields=["name", "account_name", "account_number", "parent_account",
"root_type", "account_type", "is_group"],
order_by="lft",
)
children_of = {}
roots = []
for a in accounts:
if not a.parent_account:
roots.append(a)
else:
children_of.setdefault(a.parent_account, []).append(a)
tree = {}
for root in roots:
key = _mask(root.account_name, root.account_number or "")
tree[key] = _build_node(root, children_of)
return {
"country_code": "az",
"name": "Azerbaijan Republic",
"tree": tree,
}

View File

@ -32,6 +32,7 @@ doctype_js = {
doctype_list_js = {
"Currency Exchange": "public/js/currency_exchange_list.js",
"Salary Component": "public/js/salary_component_list.js",
"Account": "public/js/account_list.js",
}
extend_doctype_class = {

View File

@ -0,0 +1,20 @@
frappe.listview_settings["Account"] = {
onload: function (listview) {
listview.page.add_inner_button(__("Export Chart of Accounts"), function () {
frappe.call({
method: "jey_erp.custom.account_export.export_chart_of_accounts",
callback: function (r) {
if (!r.message) return;
const json = JSON.stringify(r.message, null, 2);
const blob = new Blob([json], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "az_chart_of_accounts.json";
a.click();
URL.revokeObjectURL(url);
},
});
});
},
};

View File

@ -1,5 +1,29 @@
frappe.provide("frappe.treeview_settings");
function _download_chart_of_accounts() {
frappe.call({
method: "jey_erp.custom.account_export.export_chart_of_accounts",
callback: function (r) {
if (!r.message) return;
const json = JSON.stringify(r.message, null, 2);
const blob = new Blob([json], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "az_chart_of_accounts.json";
a.click();
URL.revokeObjectURL(url);
},
});
}
frappe.treeview_settings["Account"] = frappe.treeview_settings["Account"] || {};
frappe.treeview_settings["Account"].onload = function (treeview) {
treeview.page.add_inner_button(__("Export Chart of Accounts"), function () {
_download_chart_of_accounts();
});
};
// Сохраняем оригинальные поля
var original_fields = frappe.treeview_settings["Account"].fields;