fix(bank-integration): no checkbox flicker + keep "use original description" on mode change
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) <noreply@anthropic.com>
This commit is contained in:
parent
71e2c06a35
commit
6c395f9e3b
|
|
@ -1,16 +1,17 @@
|
||||||
// Bank Reconciliation Tool extensions for jey_erp.
|
// Bank Reconciliation Tool extensions for jey_erp.
|
||||||
//
|
//
|
||||||
// (1) refresh — patches DataTableManager prototype once to add a
|
// All wiring happens via a one-time DataTableManager prototype patch (on the
|
||||||
// "Bank Transaction" column (BT name with a link).
|
// form's refresh):
|
||||||
// (2) render — fires on every "Get Unreconciled Entries"; rebuilds the
|
// - get_dt_columns / format_row : adds a "Bank Transaction" column (BT name + link)
|
||||||
// DataTable with a checkbox column and injects a "Create & Reconcile"
|
// - get_datatable : creates the table WITH a checkbox column and
|
||||||
// toolbar. Selected rows are mapped to Payment Entry / Journal Entry
|
// injects the "Create & Reconcile" toolbar
|
||||||
// using either a Bank Integration record's mappings or Kapital Bank
|
// (done here so there is no destroy/rebuild flicker)
|
||||||
// Settings' mappings (chosen in the dialog; default comes from the
|
|
||||||
// Bank Account's hidden bank_integration field, written back on submit).
|
|
||||||
//
|
//
|
||||||
// The dialog / mapping logic is a port of kapital_bank's BRT extension,
|
// "Create & Reconcile" maps the selected Bank Transactions to Payment Entry /
|
||||||
// generalised to pick the mapping source.
|
// 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", {
|
frappe.ui.form.on("Bank Reconciliation Tool", {
|
||||||
refresh(frm) {
|
refresh(frm) {
|
||||||
|
|
@ -21,6 +22,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
||||||
if (!DTM || DTM.prototype._jey_erp_patched) return;
|
if (!DTM || DTM.prototype._jey_erp_patched) return;
|
||||||
const proto = DTM.prototype;
|
const proto = DTM.prototype;
|
||||||
|
|
||||||
|
// --- "Bank Transaction" column ---
|
||||||
const orig_get_dt_columns = proto.get_dt_columns;
|
const orig_get_dt_columns = proto.get_dt_columns;
|
||||||
proto.get_dt_columns = function () {
|
proto.get_dt_columns = function () {
|
||||||
orig_get_dt_columns.call(this);
|
orig_get_dt_columns.call(this);
|
||||||
|
|
@ -42,43 +44,37 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
||||||
return result;
|
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;
|
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 = {
|
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) {
|
injectToolbar: function (dtm) {
|
||||||
$("#bi-brt-toolbar").remove();
|
$("#bi-brt-toolbar").remove();
|
||||||
|
|
||||||
|
|
@ -126,15 +122,14 @@ const BIBRT = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const frm = dtm._bi_frm;
|
const bankAccount = dtm.bank_account;
|
||||||
const bankAccount = frm.doc.bank_account;
|
|
||||||
if (!bankAccount) {
|
if (!bankAccount) {
|
||||||
frappe.msgprint({ title: __("Bank Account Required"), indicator: "orange",
|
frappe.msgprint({ title: __("Bank Account Required"), indicator: "orange",
|
||||||
message: __("Pick a Bank Account in the form first.") });
|
message: __("Pick a Bank Account in the form first.") });
|
||||||
return;
|
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
|
// [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)
|
// [6]unallocated [7]reference_number [8]bank_transaction_name(string) [9]actions(button HTML)
|
||||||
const txns = [];
|
const txns = [];
|
||||||
|
|
@ -178,7 +173,6 @@ const BIBRT = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build mapping-source options + load BA default
|
|
||||||
Promise.all([
|
Promise.all([
|
||||||
frappe.db.get_list("Bank Integration", { fields: ["name", "bank_name"], limit: 200 }),
|
frappe.db.get_list("Bank Integration", { fields: ["name", "bank_name"], limit: 200 }),
|
||||||
frappe.db.get_value("Bank Account", bankAccount, ["bank_integration_type", "bank_integration"]),
|
frappe.db.get_value("Bank Account", bankAccount, ["bank_integration_type", "bank_integration"]),
|
||||||
|
|
@ -187,7 +181,7 @@ const BIBRT = {
|
||||||
const baFields = (baResp && baResp.message) || {};
|
const baFields = (baResp && baResp.message) || {};
|
||||||
const kbInstalled = kbList && kbList.length > 0;
|
const kbInstalled = kbList && kbList.length > 0;
|
||||||
|
|
||||||
const sourceMap = {}; // value -> label
|
const sourceMap = {};
|
||||||
(integrations || []).forEach(i => {
|
(integrations || []).forEach(i => {
|
||||||
sourceMap["Bank Integration::" + i.name] = __("Bank Integration: {0}", [i.bank_name || i.name]);
|
sourceMap["Bank Integration::" + i.name] = __("Bank Integration: {0}", [i.bank_name || i.name]);
|
||||||
});
|
});
|
||||||
|
|
@ -208,12 +202,11 @@ const BIBRT = {
|
||||||
if (sourceMap[candidate]) defaultSource = candidate;
|
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) {
|
_renderDialog: function (dtm, bankAccount, txns, skippedCount, sourceMap, sourceValues, defaultSource) {
|
||||||
// Preview table (deduped)
|
|
||||||
const purposeMap = new Map();
|
const purposeMap = new Map();
|
||||||
txns.forEach(function (txn) {
|
txns.forEach(function (txn) {
|
||||||
const key = txn.purpose + "|" + txn.drcr;
|
const key = txn.purpose + "|" + txn.drcr;
|
||||||
|
|
@ -340,13 +333,12 @@ const BIBRT = {
|
||||||
const [sourceType, sourceName] = values.source.split("::");
|
const [sourceType, sourceName] = values.source.split("::");
|
||||||
d.disable_primary_action();
|
d.disable_primary_action();
|
||||||
|
|
||||||
// Persist the chosen source on the Bank Account, then run the mapping/reconcile
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "jey_erp.bank_integration.import_api.update_bank_account_bi_default",
|
method: "jey_erp.bank_integration.import_api.update_bank_account_bi_default",
|
||||||
args: { bank_account: bankAccount, bi_type: sourceType, bi_name: sourceName },
|
args: { bank_account: bankAccount, bi_type: sourceType, bi_name: sourceName },
|
||||||
always: function () {
|
always: function () {
|
||||||
BIBRT._submit(d, finalTxns, sourceType, sourceName,
|
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();
|
d.show();
|
||||||
BIBRT._applyModeVisibility(d);
|
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 $fi = $(`.${dtm._bi_scopeClass} .dt-row-filter input.dt-filter[data-name="Description"]`);
|
||||||
const filterVal = ($fi.val() || "").trim();
|
const filterVal = ($fi.val() || "").trim();
|
||||||
if (filterVal) d.set_value("custom_purpose", filterVal);
|
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",
|
const toHide = ["preview_html", "sec_options", "document_type", "use_original_purpose",
|
||||||
"custom_purpose", "sec_accounts", "paid_from", "col_break", "paid_to"];
|
"custom_purpose", "sec_accounts", "paid_from", "col_break", "paid_to"];
|
||||||
toHide.forEach(fn => d.set_df_property(fn, "hidden", isDR ? 1 : 0));
|
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);
|
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 isKB = sourceType === "Kapital Bank Settings";
|
||||||
const method = isKB
|
const method = isKB
|
||||||
? "kapital_bank.mapping.create_purpose_mappings"
|
? "kapital_bank.mapping.create_purpose_mappings"
|
||||||
|
|
@ -415,7 +411,7 @@ const BIBRT = {
|
||||||
message: parts.join(" • ") || __("No changes"),
|
message: parts.join(" • ") || __("No changes"),
|
||||||
indicator: (res.created_mappings > 0 || res.created_docs > 0) ? "green" : "blue",
|
indicator: (res.created_mappings > 0 || res.created_docs > 0) ? "green" : "blue",
|
||||||
}, 6);
|
}, 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);
|
if (res.errors && res.errors.length) BIBRT.showErrors(res.errors);
|
||||||
},
|
},
|
||||||
error: function () {
|
error: function () {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue