[Tutorial] Adding button in Sales Order Items table to allow selecting and inserting all variants of template

In this i will show how to add a button in Sales Order Items table to allow selecting and inserting al variants of a template easily.

In the below code you will find good example to :
1 - Hooking client side scripts to existing DocTypes without touching the original code.
2- Using MultiSelectDialog to collect he users selecctions.
3- Adding button to child tables.

Step 1:
Create a custom app with any name you choose. (here i name it IFP

Step 2:
In hooks.py file add the below code :

doctype_js = {
	"Sales Order" : "public/js/sales_order.js"

this will command the system to run the mentioned file sales_order.js with the original sales order client script.
For more infomration about this please refer to
Override Client Script

you can change or add more doctypes like Purchase Order … etc.

Step 3:
create a new file in your app older under public/js and name the file sales_order.js and insert the below code in it. (change Sales Order in the begining if you will use for another DocType)

frappe.ui.form.on('Sales Order', {
	refresh(frm) {
             frm.add_custom_button(__('Template Variants'), function() {
                    }, __("Add"));*/
                    frm.fields_dict["items"].grid.add_custom_button(__('Add Template'), 
			            function() {

var add_template = function(frm){
    if (!frm.doc.customer){
        frappe.throw(__('Please select customer first'))

    new frappe.ui.form.MultiSelectDialog({
        doctype: "Item",
        target: cur_frm,
        setters: [{
            fieldname: 'item_name',
            fieldtype: 'Data',
            label: __('Item Name')
        data_fields: [],
        date_field: "creation",
        get_query() {
            return {
                filters: {
                    has_variants: ['=', '1']
        action(selections) {
            if(selections.length == 0){
                for (var ia = 0; ia < selections.length; ia=ia+1) {

                        method: "ifp.ifp_utils.get_template_variants",
                        args: {
                            template: selections[ia]
                        callback: function(res){
                            if (res && res.message){
                                for (var i = 0; i<res.message.length; i=i+1) {
                                    var tflag=0;
                                    $.each(frm.doc.items || [], function(r, row) {
                                        if (row){
                                            if (row.item_code == res.message[i].name){
                                    if (tflag==0){
                                        var row = frm.add_child("items");
                                        frappe.model.set_value(row.doctype, row.name, 'item_code', res.message[i].name)
                                        frappe.model.set_value(row.doctype, row.name, 'qty', 1)

Step 4:
in the same directory where hooks.py existing , create a new file named ifp_utils.py and insert below code in it:

from __future__ import unicode_literals
from frappe import publish_progress
import frappe
import json

def get_template_variants(template):
    return frappe.db.get_all('Item', filters = {'variant_of': template}, fields =['name'], as_list = False)

Step 5:
Install the app in your site and start to use.

Results :


I followed the steps as told by you but I am not getting the item shown.

Custom Buton

ERPNext “Add Multiple” Button

And there is also a problem that the button is showing even after the sales order is submitted.