From 326adf865a44411a4fe12bbbd2ef72ad0d3dee8b Mon Sep 17 00:00:00 2001 From: Norman King Date: Wed, 8 Jul 2026 20:36:21 -0400 Subject: [PATCH] fix(statements): restore Customer list button (merge listview_settings) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The list button was registered by reassigning frappe.listview_settings['Customer'] in a globally-loaded script, but ERPNext's own Customer list_js (loaded when the list opens) overwrote it, so the button never appeared. Register it via doctype_list_js instead — which Frappe appends after the doctype's own list_js — and merge into the existing settings (wrapping onload, preserving ERPNext's add_fields) rather than reassigning. The form button and shared ns_statements helpers stay in customer_statements.js. Co-Authored-By: Claude Opus 4.8 --- ns_app/hooks.py | 5 +++++ ns_app/public/js/customer_list.js | 20 ++++++++++++++++++++ ns_app/public/js/customer_statements.js | 11 +++-------- 3 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 ns_app/public/js/customer_list.js diff --git a/ns_app/hooks.py b/ns_app/hooks.py index 1802a5b..57f47f9 100644 --- a/ns_app/hooks.py +++ b/ns_app/hooks.py @@ -16,6 +16,11 @@ doctype_js = { "Sales Invoice": "public/js/sales_invoice.js" } +# Load on Customer list view (merges the "Generate Statements" action) +doctype_list_js = { + "Customer": "public/js/customer_list.js" +} + # Ensure custom fields exist after every migrate after_migrate = "ns_app.setup.after_migrate" diff --git a/ns_app/public/js/customer_list.js b/ns_app/public/js/customer_list.js new file mode 100644 index 0000000..ac239ed --- /dev/null +++ b/ns_app/public/js/customer_list.js @@ -0,0 +1,20 @@ +// Customer list action: "Generate Statements". Registered as a doctype_list_js +// so it loads alongside ERPNext's own Customer list settings (in app order, +// after them). We MERGE into listview_settings — preserving any existing +// onload / add_fields — instead of reassigning the object, which would clobber +// ERPNext's settings (and be clobbered by them). The shared generate/print +// helpers live on `ns_statements` (public/js/customer_statements.js). + +frappe.listview_settings["Customer"] = frappe.listview_settings["Customer"] || {}; + +(function () { + const settings = frappe.listview_settings["Customer"]; + const original_onload = settings.onload; + + settings.onload = function (listview) { + if (original_onload) original_onload(listview); + listview.page.add_inner_button(__("Generate Statements"), () => { + ns_statements.pick_and_generate(); + }); + }; +})(); diff --git a/ns_app/public/js/customer_statements.js b/ns_app/public/js/customer_statements.js index f417586..59a0bb5 100644 --- a/ns_app/public/js/customer_statements.js +++ b/ns_app/public/js/customer_statements.js @@ -6,14 +6,9 @@ frappe.provide("ns_statements"); -// ── Entry point: Customer list ─────────────────────────────────────────────── -frappe.listview_settings["Customer"] = { - onload(listview) { - listview.page.add_inner_button(__("Generate Statements"), () => { - ns_statements.pick_and_generate(); - }); - } -}; +// The Customer list button is registered separately in customer_list.js +// (a doctype_list_js) so it merges with — rather than overwrites — ERPNext's +// own listview_settings["Customer"]. Shared helpers live here on ns_statements. // ── Entry point: Customer form ─────────────────────────────────────────────── frappe.ui.form.on("Customer", {