@jinsy I have written a workaround code that may help you with bulk update. The problem is that I’m unable to test it myself at the moment, so if you want to try it yourself go ahead but let me know how it goes
To test my code, please create a new Client Script, select the Doctype and the select List in the Apply To field, then paste the code and modify the values of the top variables only.
The code will create a new menu action that functions just like the Edit action that is used for bulk update. It will also run the already registered list onload function if it exists.
The variables to modify are:
-
_doctype
which is the name of the Doctype you have selected for the Client Script
-
_actionButton
which is the name of the menu action that will be created, eg: "Edit & Update"
-
_customFieldsFns
which is the list of the custom field names as keys and the modification function for each as values. Each function will receive the doc
value that contains the values of the entry that is being updated.
// eg: "Purchase Receipt"
let _doctype = "",
// eg: "Edit & Update"
_actionButton = "",
// eg: {sum_total: function(doc) { return doc.total * 2; }}
_customFieldsFns = {};
// -----------------------------------
// DO NOT EDIT ANYTHING BELOW THIS
// -----------------------------------
let _lvs = frappe.listview_settings[_doctype] = frappe.listview_settings[_doctype] || {},
_pol = _lvs.onload;
_lvs.onload = function(me) {
if (_pol) _pol(me);
me.page.add_actions_menu_item(__(_actionButton), () => {
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(me.doctype).forEach((field_doc) => {
if (is_field_editable(field_doc)) field_mappings[field_doc.label] = Object.assign({}, field_doc);
});
me.disable_list_update = true;
_edit(me.doctype, me.get_checked_items(true), field_mappings,
() => {
me.disable_list_update = false;
me.clear_checked_items();
me.refresh();
}
);
});
function _edit(doctype, docnames, field_mappings, done) {
let field_options = Object.keys(field_mappings).sort();
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: 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({message: __('Updated custom fields'), indicator: 'blue'}, 5);
$.each(docnames, function(i, docname) {
frappe.db.get_doc(doctype, docname).then(doc => {
let dt = {};
$.each(_customFieldsFns, function(field, fn) { dt[field] = fn(doc); });
frappe.db.set_value(doctype, docname, dt);
});
});
frappe.show_alert(__('Updated successfully'));
});
},
primary_action_label: __('Update {0} records', [docnames.length]),
});
if (default_field) set_value_field(dialog);
show_help_text();
function set_value_field(dialogObj) {
const new_df = Object.assign({}, field_mappings[dialogObj.get_value('field')]);
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");
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();
}
};