Create new Item Attribute value quickly

When creating a new item variant through “create single variant” option in an item template, is there a way to add a new item attribute value on that popup itself without navigating to the specific item attribute form.


For example, if I want to include NVME as an option of the item attribute “Hard Disk Type”, How can I create it on this page itself w/o navigating back and forth. what customization should I try?

For now, the only way to do so would be to navigate to the item attribute list and add the values so that they are visible in the template. On the fly creation of the item attributes and their values through the item master UI is currently not possible.

I was able to achieve a simple solution for this issue.

If you also plan to try out this solution, You need to override the erpnext.item.show_single_variant_dialog with the edited function in your custom app’s app_include_js and include some server scripts too:

Edited JS Function
erpnext.item.create_item_attribute_dialog = function (args, callback) {
    Object.keys(args).map(function (key) {
        args[key] = args[key].toUpperCase().trim();
        if (!args[key]) delete args[key];
    });

    frappe.call({
        method: "if_item_attribute_values_exists",
        args: {
            item_attributes: args
        },
        callback: function (r) {
            console.log(r)
            if (!r.message) return;
            if (Object.keys(r.message).length) {
                var missing_args = r.message;
                let message = '';
                for (let item_attribute in r.message) {
                    message += `<li>${item_attribute}: ${r.message[item_attribute]}</li>`
                }
                frappe.confirm(`The following Item Attribute Values doesn't exist, Would you like to create them?<br>
                                                <ul>${message}</ul>`,
                    function () {
                        frappe.call({
                            method: "add_item_attribute_values",
                            args: {
                                item_attributes: missing_args
                            },
                            callback: function (r) {
                                console.log(r)
                                if (!r.exc) {
                                    callback(args);
                                }
                            }
                        })
                    })
            } else callback(args);
        }
    })
}

erpnext.item.show_single_variant_dialog = function (frm) {
    var fields = []

    for (var i = 0; i < frm.doc.attributes.length; i++) {
        var fieldtype, desc;
        var row = frm.doc.attributes[i];
        if (row.numeric_values) {
            fieldtype = "Float";
            desc = "Min Value: " + row.from_range + " , Max Value: " + row.to_range + ", in Increments of: " + row.increment
        }
        else {
            fieldtype = "Data";
            desc = ""
        }
        fields = fields.concat({
            "label": row.attribute,
            "fieldname": row.attribute,
            "fieldtype": fieldtype,
            "reqd": 0,
            "description": desc
        })
    }

    var d = new frappe.ui.Dialog({
        title: __('Create Variant'),
        fields: fields
    });

    d.set_primary_action(__('Create'), function () {
        var args = d.get_values();
        if (!args) return;
        var create_variant = (args) => {
            frappe.call({
                method: "erpnext.controllers.item_variant.get_variant",
                btn: d.get_primary_btn(),
                args: {
                    "template": frm.doc.name,
                    "args": args
                },
                callback: function (r) {
                    // returns variant item
                    if (r.message) {
                        var variant = r.message;
                        frappe.msgprint_dialog = frappe.msgprint(__("Item Variant {0} already exists with same attributes",
                            [repl('<a href="#Form/Item/%(item_encoded)s" class="strong variant-click">%(item)s</a>', {
                                item_encoded: encodeURIComponent(variant),
                                item: variant
                            })]
                        ));
                        frappe.msgprint_dialog.hide_on_page_refresh = true;
                        frappe.msgprint_dialog.$wrapper.find(".variant-click").on("click", function () {
                            d.hide();
                        });
                    } else {
                        d.hide();
                        frappe.call({
                            method: "erpnext.controllers.item_variant.create_variant",
                            args: {
                                "item": frm.doc.name,
                                "args": args
                            },
                            callback: function (r) {
                                var doclist = frappe.model.sync(r.message);
                                frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
                            }
                        });
                    }
                }
            });
        }
        erpnext.item.create_item_attribute_dialog(args, create_variant)

    });

    d.show();

    $.each(d.fields_dict, function (i, field) {

        if (field.df.fieldtype !== "Data") {
            return;
        }

        $(field.input_area).addClass("ui-front");

        var input = field.$input.get(0);
        input.awesomplete = new Awesomplete(input, {
            minChars: 0,
            maxItems: 99,
            autoFirst: true,
            list: [],
        });
        input.field = field;

        field.$input
            .on('input', function (e) {
                var term = e.target.value;
                frappe.call({
                    method: "erpnext.stock.doctype.item.item.get_item_attribute",
                    args: {
                        parent: i,
                        attribute_value: term
                    },
                    callback: function (r) {
                        if (r.message) {
                            e.target.awesomplete.list = r.message.map(function (d) { return d.attribute_value; });
                        }
                    }
                });
            })
            .on('focus', function (e) {
                $(e.target).val('').trigger('input');
            })
    });
}
Server Scripts

API Method: if_item_attribute_values_exists

item_attributes = json.loads(frappe.form_dict.item_attributes)
non_existing_item_attributes = {}

for item_attribute in item_attributes:
    item_attribute_value = item_attributes[item_attribute]
    get_item_attribute_values = frappe.get_all('Item Attribute Value', filters={'parent': item_attribute}, fields=['attribute_value'])
    item_attribute_value_array = []
    for item_attribute_value_object in get_item_attribute_values:
        item_attribute_value_array.append(item_attribute_value_object.attribute_value)
    if not (item_attribute_value in item_attribute_value_array):
        non_existing_item_attributes[item_attribute] = item_attribute_value

frappe.response['message'] = non_existing_item_attributes

API Method: add_item_attribute_values

item_attributes = json.loads(frappe.form_dict.item_attributes)

for item_attribute in item_attributes:
    item_attribute_value = item_attributes[item_attribute]
    item_attribute_doc = frappe.get_doc( "Item Attribute", item_attribute)
    row = item_attribute_doc.append('item_attribute_values', {})
    row.attribute_value = row.abbr = item_attribute_value.upper().strip()
    item_attribute_doc.save()

If you want further details, do ask me.

2 Likes