Hello team,
Adding a button to the listview for a specific doctype is straight forward.
In my case I want to dynamically add a button to the listview of doctypes determined by some configuration e.g. I could define the doctypes I want to have the button for in hooks.py or even a custom settings doctype.
For this reason, it is not possible to determine the doctype name in advance and therefore not possible to do this:
frappe.listview_settings['Doctype Name'] = {
onload: function(listview) {
listview.page.add_inner_button(__('My Action'), function() {
frappe.confirm(
'Are you sure you want to perform action?',
function() {
performAction();
}
);
});
}
};
Problem
I want to have a single js file which i can dynamically hook to and create listview buttons for.
So far I have done this:
In hooks.py:
# My Doctypes
# ------------------
my_doctypes = {
"frappe": ["Contact", "Address"],
"erpnext": ["Employee", "Customer", "Supplier", "Item", "Lead"],
"healthcare": ["Patient"],
}
# Dynamically add relevant js to the doctypes
doctype_list_js = {}
for app, doctypes in my_doctypes.items():
for doctype in doctypes:
# List view scripting
doctype_list_js[doctype] = "public/js/my_listview.js"
In my_doctypes.py:
@frappe.whitelist()
def get_my_doctypes():
installed_apps = frappe.get_installed_apps()
doctypes = frappe.get_hooks("my_doctypes")
applicable_doctypes = set()
for app in installed_apps:
if app in doctypes:
applicable_doctypes.update(doctypes[app])
return list(applicable_doctypes)
In my_listview.js:
frappe.call({
method: "path.to.my.get_my_doctypes",
callback: function (r) {
const doctypes = r.message || [];
doctypes.forEach((doctype) => {
console.log(`Adding listview settings for ${doctype}`);
frappe.listview_settings[doctype] = {
onload: function (listview) {
console.log(`loaded ${doctype} listview`);
listview.page.add_inner_button(__("Do Something"), async () => {
await doSomething(doctype);
}
);
console.log(`Button added for ${doctype}`);
},
};
});
},
});
async function doSomething(doctype) {
console.log(`Doing something for ${doctype}`);
frappe.call({
method: "path.to.my.method",
args: { doctype },
callback: function (r) {
if (r.message && r.message.length > 0) {
frappe.msgprint(__("Done."));
} else {
frappe.msgprint(__("Not done."));
}
},
});
}
It only works randomly for some doctypes and not working most of the time. Maybe I am missing something. What would be the correct and reliable method to achieve this?
Any help is highly appreciated, thanks.