Hello @DonDowner ,
I used client script(apply to List) and original parts of js code for bulk edit:
function edit(listview, docnames, field_mappings, done) {
let field_options = Object.keys(field_mappings).sort(function (a, b) { //řazení polí
return __(cstr(field_mappings[a].label)).localeCompare(
cstr(__(field_mappings[b].label))
);
});
const status_regex = /status/i;
const default_field = field_options.find((value) => status_regex.test(value));
const dialog = new frappe.ui.Dialog({
title: __("Bulk Edit"),
fields: [
{
fieldtype: "Select",
options: field_options,
default: default_field,
label: __("Field"),
fieldname: "field",
reqd: 1,
onchange: () => {
set_value_field(dialog);
},
},
{
fieldtype: "Data",
label: __("Value"),
fieldname: "value",
onchange() {
show_help_text();
},
},
],
primary_action: ({ value }) => {
const fieldname = field_mappings[dialog.get_value("field")].fieldname;
dialog.disable_primary_action();
frappe
.call({
method: "frappe.desk.doctype.bulk_update.bulk_update.submit_cancel_or_update_docs",
args: {
doctype: listview.doctype,
freeze: true,
docnames: docnames,
action: "update",
data: {
[fieldname]: value || null,
},
},
})
.then((r) => {
let failed = r.message || [];
if (failed.length && !r._server_messages) {
dialog.enable_primary_action();
frappe.throw(
__("Cannot update {0}", [
failed.map((f) => (f.bold ? f.bold() : f)).join(", "),
])
);
}
done();
dialog.hide();
frappe.show_alert(__("Updated successfully"));
});
},
primary_action_label: __("Update {0} records", [docnames.length]),
});
function set_value_field(dialogObj) {
const new_df = Object.assign({}, field_mappings[dialogObj.get_value("field")]);
/* if the field label has status in it and
if it has select fieldtype with no default value then
set a default value from the available option. */
if (
new_df.label.match(status_regex) &&
new_df.fieldtype === "Select" &&
!new_df.default
) {
let options = [];
if (typeof new_df.options === "string") {
options = new_df.options.split("\n");
}
//set second option as default if first option is an empty string
new_df.default = options[0] || options[1];
}
new_df.label = __("Value");
new_df.onchange = show_help_text;
delete new_df.depends_on;
dialogObj.replace_field("value", new_df);
show_help_text();
}
function show_help_text() {
let value = dialog.get_value("value");
if (value == null || value === "") {
dialog.set_df_property(
"value",
"description",
__("You have not entered a value. The field will be set to empty.")
);
} else {
dialog.set_df_property("value", "description", "");
}
}
dialog.refresh();
dialog.show();
}
function bulk_edit(listview){
//copy function that control editable docfields
const is_field_editable = (field_doc) => {
return (
field_doc.fieldname &&
frappe.model.is_value_type(field_doc) &&
field_doc.fieldtype !== "Read Only" &&
!field_doc.hidden &&
!field_doc.read_only &&
!field_doc.is_virtual
);
};
let field_mappings = {};
frappe.meta.get_docfields(listview.doctype).forEach((field_doc) => {
if (is_field_editable(field_doc)) {
field_mappings[field_doc.label] = Object.assign({}, field_doc);
}
});
listview.disable_list_update = true;
edit(listview,listview.get_checked_items(true), field_mappings, () => {
listview.disable_list_update = false;
listview.refresh();
});
}
and finally I extended the original list functions (Task dct in this cause):
let original_settings = frappe.listview_settings.Task || {};
let add_custom_buttons = {
refresh: function (listview){
listview.page.add_action_item(__("Edit"), () => {bulk_edit(listview);});
}
};
$.extend( true, original_settings, add_custom_buttons);