Not Working: Custom Script to re-order doc.items in Sales Invoice

I are trying to group the items in the Sales Invoice based on the Item Template (variant_of) and the unit_rate.
I have created a field ‘Grouped Items’ in Sales Invoice DocType as follows:

Next we created the Custom Script as follows:

      frappe.ui.form.on('Sales Invoice', {
            	validate: function(frm) {	
            		var grouped_list = []
            		var already_filtered = [];

            		// Loops through all items of array
            		for(var i = 0, len1=frm.doc.items.length; i < len1; i++) {

            			// Skip rest of the loop if the item has been grouped with some previous item
            			if(already_filtered.indexOf(i) != -1) {
            				continue;
            			}

            			// Object to hold item temporary before grouping
            			var grouped_item = {
            				item_code : frm.doc.items[i].item_variant_of,
            				item_name : [frm.doc.items[i].item_name],
            				gst_hsn_code: frm.doc.items[i].gst_hsn_code,
            				qty : frm.doc.items[i].qty,
            				net_amount: frm.doc.items[i].net_amount
            			};


            			// Loop to check the current item with rest of the items
            			for(var j = i+1; j < len1; j++) {

            				// Skip this loop if the current item has been grouped with some previous item.
            				if(already_filtered.indexOf(j) != -1) {
            					continue;
            				}

            				// Check if item_variant_of and net_rate is equal of 2 items.
            				if(frm.doc.items[i].item_variant_of === frm.doc.items[j].item_variant_of && frm.doc.items[i].net_rate === frm.doc.items[j].net_rate) {
            					// combine item_name
            					grouped_item.item_name.push(frm.doc.items[j].item_name);

            					// Add item quantity
            					grouped_item.qty += frm.doc.items[j].qty;

            					// Add net amount
            					grouped_item.net_amount += frm.doc.items[j].net_amount;

            					// Push the item to already_filtered array so it can be skipped later while comparing with rest of the items
            					already_filtered.push(j);
            				}
            			}

            			// Join item names array to show as string separated with ,
            			grouped_item.item_name = grouped_item.item_name.join(', ');

            			// Add the grouped item to the final output list
            			grouped_list.push(grouped_item);

            		}


            		frm.set_value('grouped_items', grouped_list);	
            	}
            })

The error log is as follows, please share what may be the issue:

Traceback (most recent call last):
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/desk/form/save.py", line 12, in savedocs
        doc = frappe.get_doc(json.loads(doc))
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/__init__.py", line 638, in get_doc
        return frappe.model.document.get_doc(*args, **kwargs)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/model/document.py", line 68, in get_doc
        return controller(*args, **kwargs)
      File "/home/frappe/benches/bench-2018-05-15/apps/erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.py", line 31, in __init__
        super(SalesInvoice, self).__init__(*args, **kwargs)
      File "/home/frappe/benches/bench-2018-05-15/apps/erpnext/erpnext/controllers/accounts_controller.py", line 19, in __init__
        super(AccountsController, self).__init__(*args, **kwargs)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/model/document.py", line 112, in __init__
        super(Document, self).__init__(kwargs)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/model/base_document.py", line 54, in __init__
        self.update(d)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/model/base_document.py", line 77, in update
        self.set(key, value)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/model/base_document.py", line 124, in set
        self.extend(key, value)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/model/base_document.py", line 152, in extend
        self.append(key, v)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/model/base_document.py", line 138, in append
        value = self._init_child(value, key)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/model/base_document.py", line 166, in _init_child
        raise AttributeError(key)
    AttributeError: grouped_items

    Traceback (most recent call last):
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/app.py", line 62, in application
        response = frappe.handler.handle()
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/handler.py", line 22, in handle
        data = execute_cmd(cmd)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/handler.py", line 53, in execute_cmd
        return frappe.call(method, **frappe.form_dict)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/__init__.py", line 939, in call
        return fn(*args, **newargs)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/desk/form/save.py", line 12, in savedocs
        doc = frappe.get_doc(json.loads(doc))
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/__init__.py", line 638, in get_doc
        return frappe.model.document.get_doc(*args, **kwargs)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/model/document.py", line 68, in get_doc
        return controller(*args, **kwargs)
      File "/home/frappe/benches/bench-2018-05-15/apps/erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.py", line 31, in __init__
        super(SalesInvoice, self).__init__(*args, **kwargs)
      File "/home/frappe/benches/bench-2018-05-15/apps/erpnext/erpnext/controllers/accounts_controller.py", line 19, in __init__
        super(AccountsController, self).__init__(*args, **kwargs)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/model/document.py", line 112, in __init__
        super(Document, self).__init__(kwargs)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/model/base_document.py", line 54, in __init__
        self.update(d)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/model/base_document.py", line 77, in update
        self.set(key, value)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/model/base_document.py", line 124, in set
        self.extend(key, value)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/model/base_document.py", line 152, in extend
        self.append(key, v)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/model/base_document.py", line 138, in append
        value = self._init_child(value, key)
      File "/home/frappe/benches/bench-2018-05-15/apps/frappe/frappe/model/base_document.py", line 166, in _init_child
        raise AttributeError(key)
    AttributeError: grouped_items

try below code instead

frm.set_value('grouped_items', JSON.stringify(grouped_list));

Thanks Rohit.

While using JSON.stringify, I couldn’t loop over the array because it stored as string.

Secondly, the numbers which are say (Rs). I cannot use the get_formatted function also since its a string.

It would be best if it is sent as array, rather than string. How should I go about it?

Try json.loads to convert string to object

Try

{%- for e in json.loads(doc.grouped_items) -%}
	{{ frappe.format_value(e.net_amount, df = "Currency", currency = doc.currency) }}
{%- endfor -%}

Please share your script when you are done, this is very interesting use case!

Here is the final script

frappe.ui.form.on('Sales Invoice', {
validate: function(frm) {
	
	var grouped_list = []
	var already_filtered = [];

	// Loops through all items of array
	for(var i = 0, len1=frm.doc.items.length; i < len1; i++) {

		// Skip rest of the loop if the item has been grouped with some previous item
		if(already_filtered.indexOf(i) != -1) {
			continue;
		}

		// Split item name by - , to separate name color size
		var item_name_formats = frm.doc.items[i].item_name.split(' - ');

		// Create an object with item color as key and item size as value
		var item_variance = {};
		item_variance[item_name_formats[2]] = [item_name_formats[3]];

		// Object to hold item temporary before grouping
		var grouped_item = {
			idx : i,
			item_code : frm.doc.items[i].item_variant_of,
			item_name : [item_name_formats[0] + ' - ' + item_name_formats[1]],
			gst_hsn_code: frm.doc.items[i].gst_hsn_code,
			qty : frm.doc.items[i].qty,
			uom : frm.doc.items[i].uom,
		        net_rate : frm.doc.items[i].net_rate,
			net_amount: frm.doc.items[i].net_amount
		};


		// Loop to check the current item with rest of the items
		for(var j = i+1; j < len1; j++) {

			// Skip this loop if the current item has been grouped with some previous item.
			if(already_filtered.indexOf(j) != -1) {
				continue;
			}

			// Check if item_variant_of and net_rate is equal of 2 items.
			if(frm.doc.items[i].item_variant_of === frm.doc.items[j].item_variant_of && frm.doc.items[i].net_rate === frm.doc.items[j].net_rate) {
				
				// Add item quantity
				grouped_item.qty += frm.doc.items[j].qty;

				// Add net amount
				grouped_item.net_amount += frm.doc.items[j].net_amount;

				// Split item name by - , to separate name color size
				var next_item_name_formats = frm.doc.items[j].item_name.split(' - ');

				// if this color is already present, add 1 more size to it
				// else create another key for another color
				if(item_variance[next_item_name_formats[2]]) {
					item_variance[next_item_name_formats[2]].push(next_item_name_formats[3]);
				}
				else {
					item_variance[next_item_name_formats[2]] = [next_item_name_formats[3]]
				}

				// Push the item to already_filtered array so it can be skipped later while comparing with rest of the items
				already_filtered.push(j);
			}
		}

		// Loop through all colors and join sizes by ,
		var variances = '';
		for(var key in item_variance) {
			variances += "<br >" + key + ' - ' + item_variance[key].join(', ')
		}

		// Join item names with each variance
		grouped_item.item_name = grouped_item.item_name + variances;

		// Add the grouped item to the final output list
		grouped_list.push(grouped_item);

	}
		frm.set_value('grouped_items', JSON.stringify(grouped_list));

}
})
1 Like