From 6c395f9e3be4ea03fe9124a4814dc631129e6b8d Mon Sep 17 00:00:00 2001 From: Ali <010109ali@gmail.com> Date: Mon, 11 May 2026 13:21:52 +0000 Subject: [PATCH] fix(bank-integration): no checkbox flicker + keep "use original description" on mode change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two BRT polish fixes: - The checkbox column was added by destroying and rebuilding the DataTable after each "Get Unreconciled Entries", which visibly redrew the rows. Replaced with a wholesale override of DataTableManager's get_datatable that creates the table with checkboxColumn:true from the start and injects the toolbar there — no rebuild, no flicker. The render-event polling is gone. - _applyModeVisibility unconditionally un-hid "Custom Purpose Keyword" when leaving "Documents & Reconcile" mode, overriding the effect of the "Use original description" checkbox. Now it re-applies the checkbox state after the mode-driven visibility pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- jey_erp/public/js/bank_reconciliation_tool.js | 104 +++++++++--------- 1 file changed, 50 insertions(+), 54 deletions(-) diff --git a/jey_erp/public/js/bank_reconciliation_tool.js b/jey_erp/public/js/bank_reconciliation_tool.js index 6aa49a5..1139d9a 100644 --- a/jey_erp/public/js/bank_reconciliation_tool.js +++ b/jey_erp/public/js/bank_reconciliation_tool.js @@ -1,16 +1,17 @@ // Bank Reconciliation Tool extensions for jey_erp. // -// (1) refresh — patches DataTableManager prototype once to add a -// "Bank Transaction" column (BT name with a link). -// (2) render — fires on every "Get Unreconciled Entries"; rebuilds the -// DataTable with a checkbox column and injects a "Create & Reconcile" -// toolbar. Selected rows are mapped to Payment Entry / Journal Entry -// using either a Bank Integration record's mappings or Kapital Bank -// Settings' mappings (chosen in the dialog; default comes from the -// Bank Account's hidden bank_integration field, written back on submit). +// All wiring happens via a one-time DataTableManager prototype patch (on the +// form's refresh): +// - get_dt_columns / format_row : adds a "Bank Transaction" column (BT name + link) +// - get_datatable : creates the table WITH a checkbox column and +// injects the "Create & Reconcile" toolbar +// (done here so there is no destroy/rebuild flicker) // -// The dialog / mapping logic is a port of kapital_bank's BRT extension, -// generalised to pick the mapping source. +// "Create & Reconcile" maps the selected Bank Transactions to Payment Entry / +// Journal Entry using a chosen mapping source: a Bank Integration record +// (jey_erp) or Kapital Bank Settings (kapital_bank). The default source comes +// from the Bank Account's hidden bank_integration field and is written back on +// submit. The dialog / mapping logic is a port of kapital_bank's BRT extension. frappe.ui.form.on("Bank Reconciliation Tool", { refresh(frm) { @@ -21,6 +22,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", { if (!DTM || DTM.prototype._jey_erp_patched) return; const proto = DTM.prototype; + // --- "Bank Transaction" column --- const orig_get_dt_columns = proto.get_dt_columns; proto.get_dt_columns = function () { orig_get_dt_columns.call(this); @@ -42,43 +44,37 @@ frappe.ui.form.on("Bank Reconciliation Tool", { return result; }; + // --- checkbox column + Create & Reconcile toolbar --- + // Replace get_datatable wholesale so the table is built with + // checkboxColumn:true from the start (no visible rebuild). + proto.get_datatable = function () { + this.datatable = new frappe.DataTable(this.$reconciliation_tool_dt.get(0), { + columns: this.columns, + data: this.transactions, + dynamicRowHeight: true, + checkboxColumn: true, + inlineFilters: true, + }); + $(`.${this.datatable.style.scopeClass} .dt-scrollable`).css("max-height", "calc(100vh - 400px)"); + + if (this.transactions.length > 0) { + this.$reconciliation_tool_dt.show(); + this.$no_bank_transactions.hide(); + } else { + this.$reconciliation_tool_dt.hide(); + this.$no_bank_transactions.show(); + } + + this._bi_scopeClass = this.datatable.style.scopeClass; + BIBRT.injectToolbar(this); + }; + proto._jey_erp_patched = true; }); }, - - render(frm) { - if (frm._bi_check_interval) clearInterval(frm._bi_check_interval); - frm._bi_check_interval = setInterval(function () { - const dtm = frm.bank_reconciliation_data_table_manager; - if (dtm && dtm.datatable && !dtm._bi_enhanced) { - clearInterval(frm._bi_check_interval); - BIBRT.enhance(frm, dtm); - } - }, 200); - setTimeout(function () { clearInterval(frm._bi_check_interval); }, 15000); - }, }); const BIBRT = { - enhance: function (frm, dtm) { - dtm._bi_enhanced = true; - dtm._bi_frm = frm; - - dtm.datatable.destroy(); - dtm.datatable = new frappe.DataTable(dtm.$reconciliation_tool_dt.get(0), { - columns: dtm.columns, - data: dtm.transactions, - dynamicRowHeight: true, - checkboxColumn: true, - inlineFilters: true, - }); - $(`.${dtm.datatable.style.scopeClass} .dt-scrollable`).css("max-height", "calc(100vh - 400px)"); - dtm.set_listeners(); - dtm._bi_scopeClass = dtm.datatable.style.scopeClass; - - BIBRT.injectToolbar(dtm); - }, - injectToolbar: function (dtm) { $("#bi-brt-toolbar").remove(); @@ -126,15 +122,14 @@ const BIBRT = { return; } - const frm = dtm._bi_frm; - const bankAccount = frm.doc.bank_account; + const bankAccount = dtm.bank_account; if (!bankAccount) { frappe.msgprint({ title: __("Bank Account Required"), indicator: "orange", message: __("Pick a Bank Account in the form first.") }); return; } - // dtm.transactions rows are arrays. With the jey_erp prototype patch active the layout is: + // dtm.transactions rows are arrays. With the jey_erp prototype patch active: // [0]date [1]party_type [2]party(HTML) [3]description [4]deposit [5]withdrawal // [6]unallocated [7]reference_number [8]bank_transaction_name(string) [9]actions(button HTML) const txns = []; @@ -178,7 +173,6 @@ const BIBRT = { return; } - // Build mapping-source options + load BA default Promise.all([ frappe.db.get_list("Bank Integration", { fields: ["name", "bank_name"], limit: 200 }), frappe.db.get_value("Bank Account", bankAccount, ["bank_integration_type", "bank_integration"]), @@ -187,7 +181,7 @@ const BIBRT = { const baFields = (baResp && baResp.message) || {}; const kbInstalled = kbList && kbList.length > 0; - const sourceMap = {}; // value -> label + const sourceMap = {}; (integrations || []).forEach(i => { sourceMap["Bank Integration::" + i.name] = __("Bank Integration: {0}", [i.bank_name || i.name]); }); @@ -208,12 +202,11 @@ const BIBRT = { if (sourceMap[candidate]) defaultSource = candidate; } - BIBRT._renderDialog(dtm, frm, bankAccount, txns, skippedCount, sourceMap, sourceValues, defaultSource); + BIBRT._renderDialog(dtm, bankAccount, txns, skippedCount, sourceMap, sourceValues, defaultSource); }); }, - _renderDialog: function (dtm, frm, bankAccount, txns, skippedCount, sourceMap, sourceValues, defaultSource) { - // Preview table (deduped) + _renderDialog: function (dtm, bankAccount, txns, skippedCount, sourceMap, sourceValues, defaultSource) { const purposeMap = new Map(); txns.forEach(function (txn) { const key = txn.purpose + "|" + txn.drcr; @@ -340,13 +333,12 @@ const BIBRT = { const [sourceType, sourceName] = values.source.split("::"); d.disable_primary_action(); - // Persist the chosen source on the Bank Account, then run the mapping/reconcile frappe.call({ method: "jey_erp.bank_integration.import_api.update_bank_account_bi_default", args: { bank_account: bankAccount, bi_type: sourceType, bi_name: sourceName }, always: function () { BIBRT._submit(d, finalTxns, sourceType, sourceName, - values.paid_from, values.paid_to, values.document_type, values.mode, frm); + values.paid_from, values.paid_to, values.document_type, values.mode); }, }); }, @@ -356,7 +348,6 @@ const BIBRT = { d.show(); BIBRT._applyModeVisibility(d); - // Pre-fill custom purpose from the Description inline filter if active const $fi = $(`.${dtm._bi_scopeClass} .dt-row-filter input.dt-filter[data-name="Description"]`); const filterVal = ($fi.val() || "").trim(); if (filterVal) d.set_value("custom_purpose", filterVal); @@ -367,10 +358,15 @@ const BIBRT = { const toHide = ["preview_html", "sec_options", "document_type", "use_original_purpose", "custom_purpose", "sec_accounts", "paid_from", "col_break", "paid_to"]; toHide.forEach(fn => d.set_df_property(fn, "hidden", isDR ? 1 : 0)); + // The "use original description" checkbox also hides custom_purpose — re-apply + // it so switching Action modes doesn't reveal a field the checkbox had hidden. + if (!isDR && d.get_value("use_original_purpose")) { + d.set_df_property("custom_purpose", "hidden", 1); + } d.refresh_fields(toHide); }, - _submit: function (dialog, txns, sourceType, sourceName, paid_from, paid_to, document_type, mode, frm) { + _submit: function (dialog, txns, sourceType, sourceName, paid_from, paid_to, document_type, mode) { const isKB = sourceType === "Kapital Bank Settings"; const method = isKB ? "kapital_bank.mapping.create_purpose_mappings" @@ -415,7 +411,7 @@ const BIBRT = { message: parts.join(" • ") || __("No changes"), indicator: (res.created_mappings > 0 || res.created_docs > 0) ? "green" : "blue", }, 6); - if (frm) frm.refresh(); + if (cur_frm && cur_frm.doctype === "Bank Reconciliation Tool") cur_frm.refresh(); if (res.errors && res.errors.length) BIBRT.showErrors(res.errors); }, error: function () {