No Read or Select Permissions for Item Attribute Value

Hi :slightly_smiling_face:

Created a custom dialog to select 3 parameters for creating a default BOM for a custom field custom_followup_bom in the Purchase Order Item child table. Two parameters (Pot Size and Soil Type) are Item Attribute Values:

frappe.ui.form.ControlLink.link_options = function(link) {
    if ((link.df.dt == "Purchase Order Item") &&
            (link.df.fieldname == "custom_followup_bom")) {
        let title = "Create Default BOM";
    
        return [{
            html: "<span class='link-option'>" +
                "<i class='fa fa-plus' style='margin-right: 5px;'></i> " +
                __(title) +
                "</span>",
            label: __(title),
            value: "create_default__link_option",
            action: () => {
                let d = new frappe.ui.Dialog({
                    title: title,
                    fields: [{
                        label: "Item",
                        fieldname: "item",
                        fieldtype: "Data",
                    }, {
                        label: "Pot Size",
                        fieldname: "potSz",
                        fieldtype: "Link",
                        options: "Item Attribute Value",
                        get_query() {
                            return {
                                query: "wg_erp.api.get_attr_vals",
                                filters: { "attr": "PotSize" }
                            };
                        },
                    }, {
                        label: "Sell Price",
                        fieldname: "sellPrice",
                        fieldtype: "Float",
                    }, {
                        label: "Soil Type",
                        fieldname: "soilTp",
                        fieldtype: "Link",
                        options: "Item Attribute Value",
                        get_query() {
                            return {
                                query: "wg_erp.api.get_attr_vals",
                                filters: { "attr": "SoilType" }
                            };
                        },
                    }],
                    size: "small",
                    primary_action_label: "Create",
                    primary_action(values) {
                        if (!values.potSz) {
                            frappe.msgprint("Require Pot Size", title);
                        } else if (values.sellPrice == "0") {
                            frappe.msgprint("Invalid Sell Price", title);
                        } else if (!values.soilTp) {
                            frappe.msgprint("Require Soil Type", title);
                        } else {
                            frappe.call({
                                method: "create_default_bom",
                                args: {
                                    item_code: link.doc.item_code,
                                    pot_sz: values.potSz,
                                    sell_price: values.sellPrice,
                                    soil_tp: values.soilTp,
                                },
                            }).then((success) => {
                                sm = success.message;
                                msg = sm.msgInsVar;
                                msg = (msg? msg + "<br/>": "") + sm.msg;
                                if (sm.bom_name != "")
                                    frappe.model.set_value(
                                        link.doctype, link.docname,
                                        "custom_followup_bom", sm.bom_name);
                                frappe.utils.play_sound(sm.status == "error"?
                                        "error": "submit");
                                frappe.msgprint(msg, title);
                            }, (error) => {
                                console.log(error.message);
                            });
                            d.hide();
                        }
                    },
                });

                let n = link.doc.item_code.indexOf("-");
                let itemCode = (n > -1)?
                    link.doc.item_code.substring(0, n):
                    link.doc.item_code;
                frappe.db.get_value("Item", itemCode, "custom_full_name")
                    .then(r => d.set_value("item", r.message.custom_full_name));
                d.set_df_property("item", "read_only", 1);
                
                d.show();

                setTimeout(() => {
                    $("input[data-fieldname='potSz']").focus();
                }, 600);
            },
        }];
    }
};

The two Link fields for the Item Attribute Values call a backend Python function in a custom app wg_erp.api.get_attr_vals:

import frappe

@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_attr_vals(doctype, txt, searchfield, start, page_len, filters):
    attr = filters["attr"]
    s_txt = "%" + txt.replace("'", "''") + "%" if txt != "" else "%"
    return frappe.db.sql(f"""
        select abbr, attribute_value
        from `tabItem Attribute Value`
        where (parenttype = 'Item Attribute') and
            (parent = '{attr}') and
            ((name like '{s_txt}') or (attribute_value like '{s_txt}'))
        order by abbr
    """)

When opening the dialog and the Pot Size field is focused, the values are shown nicely in the drop-down list. But when confirming a value with Enter the following message is displayed:

You do not have Read or Select Permissions for Item Attribute Value

This message does not appear for the Administrator, only for regular users. But even when assigning all Roles and Modules to a user on their Roles & Permissions tab the error pops up. Tried to assign permissions with the Role Permissions Manager, but it’s not possible for Item Attribute Value. Tried to hack the permissions table and copied an existing row with System Manager permissions:

insert into `tabCustom DocPerm` (`name`, `creation`, `modified`, `modified_by`,
    `owner`, `docstatus`, `idx`, `parent`, `role`, `if_owner`, `permlevel`,
    `select`, `read`, `write`, `create`, `delete`, `submit`, `cancel`, `amend`,
    `report`, `export`, `import`, `share`, `print`, `email`, `_user_tags`,
    `_comments`, `_assign`, `_liked_by`)
select 'ei27rc790t', now(), now(), `modified_by`, `owner`, `docstatus`, 0,
    'Item Attribute Value', `role`, `if_owner`, `permlevel`, `select`, `read`,
    `write`, `create`, `delete`, `submit`, `cancel`, `amend`, `report`, `export`,
    `import`, `share`, `print`, `email`, `_user_tags`, `_comments`, `_assign`,
    `_liked_by`
from `tabCustom DocPerm`
where (`name` = 'ei27rc790s');

and the permissions then showed up in Role Permissions Manager also, but the error persists.

Any hints to resolve this are highly appreciated!

@legolas108 assign permission of doctype item attribute

Thanks much for speedy reply. Assigned all (except Import) permissions on Item Attribute to the role the user is assigned to, but problem persists :thinking:

Forgot to provide versions in use:

ERPNext: v15.33.2 (version-15)
Frappe Framework: v15.38.0 (version-15)

OK, seem to have found what looks like a bug.

In the Frappe app, frappe/client.py, it says:

@frappe.whitelist()
def validate_link(doctype: str, docname: str, fields=None):
	if not isinstance(doctype, str):
		frappe.throw(_("DocType must be a string"))

	if not isinstance(docname, str):
		frappe.throw(_("Document Name must be a string"))

	if doctype != "DocType" and not (
		frappe.has_permission(doctype, "select") or frappe.has_permission(doctype, "read")
	):
		frappe.throw(
			_("You do not have Read or Select Permissions for {}").format(frappe.bold(doctype)),
			frappe.PermissionError,
		)

	values = frappe._dict()
...

frappe.has_permission expects a parent_doctype parameter for child tables which validate_link doesn’t provide, seemingly written without having child tables in mind. Changing the calls to:

	parent_doctype = "Item Attribute" if doctype == "Item Attribute Value" else None
	if doctype != "DocType" and not (
		frappe.has_permission(doctype, "select", parent_doctype = parent_doctype) or
		frappe.has_permission(doctype, "read", parent_doctype = parent_doctype)
	):

makes things work as expected. Of course this should be generalized to cover all child tables.