added bulk edit to kb settings
This commit is contained in:
parent
126c455217
commit
6779cf89ec
|
|
@ -146,6 +146,17 @@ frappe.ui.form.on('Kapital Bank Settings', {
|
||||||
}
|
}
|
||||||
}, __('Purpose'));
|
}, __('Purpose'));
|
||||||
|
|
||||||
|
// ── Bulk Edit buttons for mapping tables ─────────────────────────────
|
||||||
|
const mapping_tables = [
|
||||||
|
'account_mappings', 'card_mappings', 'customer_mappings',
|
||||||
|
'supplier_mappings', 'transaction_mappings'
|
||||||
|
];
|
||||||
|
mapping_tables.forEach(table => {
|
||||||
|
frm.fields_dict[table]?.grid.add_custom_button(__('Bulk Edit'), () => {
|
||||||
|
bulk_edit_table(frm, table);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// ── Populate data tabs ────────────────────────────────────────────────
|
// ── Populate data tabs ────────────────────────────────────────────────
|
||||||
load_data_tabs(frm);
|
load_data_tabs(frm);
|
||||||
}
|
}
|
||||||
|
|
@ -1029,6 +1040,198 @@ function render_purposes_list(purposes, total_count) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
// BULK EDIT
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
function bulk_edit_table(frm, table_field) {
|
||||||
|
const selected_rows = frm.fields_dict[table_field].grid.get_selected_children();
|
||||||
|
|
||||||
|
if (selected_rows.length === 0) {
|
||||||
|
frappe.msgprint(__("No rows selected. Please select rows to edit."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const child_doctype = selected_rows[0].doctype;
|
||||||
|
const meta = frappe.get_meta(child_doctype);
|
||||||
|
|
||||||
|
const field_options = [];
|
||||||
|
const field_map = {};
|
||||||
|
|
||||||
|
meta.fields.forEach(df => {
|
||||||
|
if (!['Section Break', 'Column Break', 'Tab Break', 'HTML', 'Table', 'Button'].includes(df.fieldtype)
|
||||||
|
&& !df.read_only && !df.hidden) {
|
||||||
|
field_options.push({
|
||||||
|
label: __(df.label || frappe.model.unscrub(df.fieldname)),
|
||||||
|
value: df.fieldname
|
||||||
|
});
|
||||||
|
field_map[df.fieldname] = df;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const d = new frappe.ui.Dialog({
|
||||||
|
title: __("Bulk Edit Selected Rows"),
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
label: __("Field to Edit"),
|
||||||
|
fieldname: "field_to_edit",
|
||||||
|
fieldtype: "Select",
|
||||||
|
options: field_options,
|
||||||
|
reqd: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "value_section",
|
||||||
|
fieldtype: "Section Break",
|
||||||
|
label: __("New Value"),
|
||||||
|
collapsible: 0,
|
||||||
|
hidden: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "value_container",
|
||||||
|
fieldtype: "HTML",
|
||||||
|
options: '<div id="value_field_container"></div>'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
primary_action_label: __("Apply"),
|
||||||
|
primary_action() {
|
||||||
|
const field_name = d.get_value("field_to_edit");
|
||||||
|
const field_def = field_map[field_name];
|
||||||
|
|
||||||
|
if (!field_name || !d.new_value_field) {
|
||||||
|
frappe.msgprint(__("Please select a field and enter a value."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const new_value = d.new_value_field.get_value();
|
||||||
|
|
||||||
|
if (new_value === undefined || new_value === null || new_value === "") {
|
||||||
|
frappe.msgprint(__("Please enter a new value."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Dynamic Link, also set the type field
|
||||||
|
const is_dynamic = field_def && field_def.fieldtype === "Dynamic Link";
|
||||||
|
const type_field = is_dynamic ? field_def.options : null;
|
||||||
|
const type_value = is_dynamic && d.dynamic_link_type_field
|
||||||
|
? d.dynamic_link_type_field.get_value() : null;
|
||||||
|
|
||||||
|
if (is_dynamic && !type_value) {
|
||||||
|
frappe.msgprint(__("Please select a type."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = 0;
|
||||||
|
const promises = [];
|
||||||
|
|
||||||
|
selected_rows.forEach(row => {
|
||||||
|
if (is_dynamic && type_field) {
|
||||||
|
promises.push(
|
||||||
|
frappe.model.set_value(row.doctype, row.name, type_field, type_value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
promises.push(
|
||||||
|
frappe.model.set_value(row.doctype, row.name, field_name, new_value)
|
||||||
|
.then(() => { count++; })
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Promise.all(promises).then(() => {
|
||||||
|
frm.refresh_field(table_field);
|
||||||
|
frappe.show_alert({
|
||||||
|
message: __("Updated {0} rows", [count]),
|
||||||
|
indicator: 'green'
|
||||||
|
}, 5);
|
||||||
|
d.hide();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function clear_value_controls() {
|
||||||
|
if (d.new_value_field) {
|
||||||
|
d.new_value_field.$wrapper.remove();
|
||||||
|
delete d.new_value_field;
|
||||||
|
}
|
||||||
|
if (d.dynamic_link_type_field) {
|
||||||
|
d.dynamic_link_type_field.$wrapper.remove();
|
||||||
|
delete d.dynamic_link_type_field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_value_control(field_def, container) {
|
||||||
|
d.new_value_field = frappe.ui.form.make_control({
|
||||||
|
df: {
|
||||||
|
label: __("New Value for {0}", [__(field_def.label || frappe.model.unscrub(field_def.fieldname))]),
|
||||||
|
fieldname: "new_value",
|
||||||
|
fieldtype: field_def.fieldtype === "Dynamic Link" ? "Link" : field_def.fieldtype,
|
||||||
|
options: field_def.options || "",
|
||||||
|
reqd: 1
|
||||||
|
},
|
||||||
|
parent: container,
|
||||||
|
render_input: true
|
||||||
|
});
|
||||||
|
d.new_value_field.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
d.fields_dict.field_to_edit.$input.on('change', function() {
|
||||||
|
const field_name = d.get_value("field_to_edit");
|
||||||
|
const field_def = field_map[field_name];
|
||||||
|
|
||||||
|
if (!field_def) return;
|
||||||
|
|
||||||
|
d.set_df_property("value_section", "hidden", 0);
|
||||||
|
clear_value_controls();
|
||||||
|
|
||||||
|
const value_container = d.fields_dict.value_container.$wrapper.find("#value_field_container");
|
||||||
|
|
||||||
|
if (field_def.fieldtype === "Dynamic Link") {
|
||||||
|
// Find the linked Select field (e.g. counterparty_type for counterparty)
|
||||||
|
const type_df = field_map[field_def.options];
|
||||||
|
if (type_df) {
|
||||||
|
d.dynamic_link_type_field = frappe.ui.form.make_control({
|
||||||
|
df: {
|
||||||
|
label: __(type_df.label || frappe.model.unscrub(type_df.fieldname)),
|
||||||
|
fieldname: "dynamic_link_type",
|
||||||
|
fieldtype: type_df.fieldtype,
|
||||||
|
options: type_df.options || "",
|
||||||
|
reqd: 1
|
||||||
|
},
|
||||||
|
parent: value_container,
|
||||||
|
render_input: true
|
||||||
|
});
|
||||||
|
d.dynamic_link_type_field.refresh();
|
||||||
|
|
||||||
|
d.dynamic_link_type_field.$input.on('change', () => {
|
||||||
|
const selected_type = d.dynamic_link_type_field.get_value();
|
||||||
|
if (d.new_value_field) {
|
||||||
|
d.new_value_field.$wrapper.remove();
|
||||||
|
delete d.new_value_field;
|
||||||
|
}
|
||||||
|
if (selected_type) {
|
||||||
|
d.new_value_field = frappe.ui.form.make_control({
|
||||||
|
df: {
|
||||||
|
label: __("New Value for {0}", [__(field_def.label || frappe.model.unscrub(field_def.fieldname))]),
|
||||||
|
fieldname: "new_value",
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: selected_type,
|
||||||
|
reqd: 1
|
||||||
|
},
|
||||||
|
parent: value_container,
|
||||||
|
render_input: true
|
||||||
|
});
|
||||||
|
d.new_value_field.refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
make_value_control(field_def, value_container);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
make_value_control(field_def, value_container);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
d.show();
|
||||||
|
}
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════════
|
||||||
// HELPERS
|
// HELPERS
|
||||||
// ═══════════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"actions": [],
|
"actions": [],
|
||||||
"creation": "2026-02-24 00:00:00.000000",
|
"creation": "2026-02-24 00:00:00",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
|
|
@ -219,8 +219,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "data_tab_html",
|
"fieldname": "data_tab_html",
|
||||||
"fieldtype": "HTML",
|
"fieldtype": "HTML"
|
||||||
"options": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "mappings_tab",
|
"fieldname": "mappings_tab",
|
||||||
|
|
@ -229,8 +228,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "mappings_tab_html",
|
"fieldname": "mappings_tab_html",
|
||||||
"fieldtype": "HTML",
|
"fieldtype": "HTML"
|
||||||
"options": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "account_mappings_tab",
|
"fieldname": "account_mappings_tab",
|
||||||
|
|
@ -244,6 +242,7 @@
|
||||||
"label": "Account Mappings"
|
"label": "Account Mappings"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_bulk_edit": 1,
|
||||||
"fieldname": "account_mappings",
|
"fieldname": "account_mappings",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "Account Mappings",
|
"label": "Account Mappings",
|
||||||
|
|
@ -261,6 +260,7 @@
|
||||||
"label": "Card Mappings"
|
"label": "Card Mappings"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_bulk_edit": 1,
|
||||||
"fieldname": "card_mappings",
|
"fieldname": "card_mappings",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "Card Mappings",
|
"label": "Card Mappings",
|
||||||
|
|
@ -278,6 +278,7 @@
|
||||||
"label": "Customer Mappings"
|
"label": "Customer Mappings"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_bulk_edit": 1,
|
||||||
"fieldname": "customer_mappings",
|
"fieldname": "customer_mappings",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "Customer Mappings",
|
"label": "Customer Mappings",
|
||||||
|
|
@ -295,6 +296,7 @@
|
||||||
"label": "Supplier Mappings"
|
"label": "Supplier Mappings"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_bulk_edit": 1,
|
||||||
"fieldname": "supplier_mappings",
|
"fieldname": "supplier_mappings",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "Supplier Mappings",
|
"label": "Supplier Mappings",
|
||||||
|
|
@ -321,7 +323,7 @@
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2026-02-27 00:00:00.000000",
|
"modified": "2026-03-05 22:08:29.828228",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Kapital Bank",
|
"module": "Kapital Bank",
|
||||||
"name": "Kapital Bank Settings",
|
"name": "Kapital Bank Settings",
|
||||||
|
|
@ -331,10 +333,8 @@
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
|
||||||
"role": "System Manager",
|
"role": "System Manager",
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Copyright (c) 2026, Jey ERP and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.tests import IntegrationTestCase
|
||||||
|
|
||||||
|
|
||||||
|
# On IntegrationTestCase, the doctype test records and all
|
||||||
|
# link-field test record dependencies are recursively loaded
|
||||||
|
# Use these module variables to add/remove to/from that list
|
||||||
|
EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
|
||||||
|
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class IntegrationTestKapitalBankSettings(IntegrationTestCase):
|
||||||
|
"""
|
||||||
|
Integration tests for KapitalBankSettings.
|
||||||
|
Use this class for testing interactions between multiple components.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
Loading…
Reference in New Issue