diff --git a/frappe/public/js/frappe/roles_editor.js b/frappe/public/js/frappe/roles_editor.js index 2ca6220e6e..61aef5b2ae 100644 --- a/frappe/public/js/frappe/roles_editor.js +++ b/frappe/public/js/frappe/roles_editor.js @@ -1,13 +1,38 @@ frappe.RoleEditor = class { - constructor(wrapper, frm, disable) { + /** + * Create a role editor for a form child table. + * + * @param {HTMLElement|JQuery} wrapper Container for the MultiCheck control. + * @param {frappe.ui.form.Form} frm Form whose role rows are edited. + * @param {boolean|number|Object} [disable=false] Disable role selection inputs, or pass role row configuration as the third argument. + * @param {Object} [options] Role row configuration overrides. + * @param {string} [options.table_fieldname="roles"] Child table field containing role rows. + * @param {string} [options.role_fieldname="role"] Field in each child row that stores the role value. + * @param {string} [options.child_doctype] Child DocType used when adding role rows. Defaults to the table field's configured child DocType or "Has Role". + */ + constructor(wrapper, frm, disable = false, options = {}) { + if (disable && typeof disable === "object") { + options = disable; + disable = false; + } + + const { table_fieldname = "roles", role_fieldname = "role", child_doctype } = options; + const configured_child_doctype = frappe.meta.get_docfield( + frm.doctype, + table_fieldname + )?.options; + this.frm = frm; this.wrapper = wrapper; - this.disable = disable; - let user_roles = this.frm.doc.roles ? this.frm.doc.roles.map((a) => a.role) : []; + this.disable = Boolean(disable); + this.table_fieldname = table_fieldname; + this.role_fieldname = role_fieldname; + this.child_doctype = child_doctype || configured_child_doctype || "Has Role"; + let user_roles = this.get_selected_roles(); this.multicheck = frappe.ui.form.make_control({ parent: wrapper, df: { - fieldname: "roles", + fieldname: this.table_fieldname, fieldtype: "MultiCheck", select_all: true, columns: "15rem", @@ -130,25 +155,41 @@ frappe.RoleEditor = class { } reset() { - let user_roles = (this.frm.doc.roles || []).map((a) => a.role); + let user_roles = this.get_selected_roles(); this.multicheck.selected_options = user_roles; this.multicheck.refresh_input(); } set_roles_in_table() { - let roles = this.frm.doc.roles || []; + let roles = this.get_role_rows(); let checked_options = this.multicheck.get_checked_options(); - roles.map((role_doc) => { - if (!checked_options.includes(role_doc.role)) { + roles.forEach((role_doc) => { + if (!checked_options.includes(this.get_role_value(role_doc))) { frappe.model.clear_doc(role_doc.doctype, role_doc.name); } }); - checked_options.map((role) => { - if (!roles.find((d) => d.role === role)) { - let role_doc = frappe.model.add_child(this.frm.doc, "Has Role", "roles"); - role_doc.role = role; + checked_options.forEach((role) => { + if (!roles.find((d) => this.get_role_value(d) === role)) { + let role_doc = frappe.model.add_child( + this.frm.doc, + this.child_doctype, + this.table_fieldname + ); + this.set_role_value(role_doc, role); } }); } + get_role_rows() { + return this.frm.doc[this.table_fieldname] || []; + } + get_selected_roles() { + return this.get_role_rows().map((row) => this.get_role_value(row)); + } + get_role_value(row) { + return row[this.role_fieldname]; + } + set_role_value(row, role) { + row[this.role_fieldname] = role; + } get_roles() { return { checked_roles: this.multicheck.get_checked_options(),