[Tutorial] How to add a Shortcut to get Item Balance with Warehouses in Sales invoice

Put this code in custom script for Sales Invoice and then use “CTRL + Q” shortcut to open the dialog box when you are in the item field, and you can choose from it the warehouse and it will update it to the item.

frappe.ui.keys.add_shortcut({
    shortcut: 'ctrl+q',
    action: () => { 
            const current_doc = $('.data-row.editable-row').parent().attr("data-name");
            const item_row = locals["Sales Invoice Item"][current_doc];
            frappe.call({
                method: 'erpnext.stock.dashboard.item_dashboard.get_data',
                args: {
                    item_code: item_row.item_code,
                },
                callback: function(r) {
                    if (r.message.length > 0){
                        const d = new frappe.ui.Dialog({
                            title: __('Item Balance'),
                            width: 400
                        });
                        $(`<div class="modal-body ui-front">
                            <h2>${item_row.item_code}</h2>
                            <p>Choose Warehouse and click Select :</p>
                            <table class="table table-bordered">
                            <thead>
                                <tr>
                                <th>Check</th>
                                <th>Warehouse</th>
                                <th>Qty</th>
                                <th>UOM</th>
                                </tr>
                            </thead>
                            <tbody>
                            </tbody>
                            </table>
                        </div>`).appendTo(d.body);
                        r.message.forEach(element => {
                            const tbody = $(d.body).find('tbody');
                            const tr = $(`
                            <tr>
                                <td><input type="checkbox" class="check-warehouse" data-warehouse="${element.warehouse}"></td>
                                <td>${element.warehouse}</td>
                                <td>${element.actual_qty}</td>
                                <td>${item_row.stock_uom }</td>
                            </tr>
                            `).appendTo(tbody)
                            tbody.find('.check-warehouse').on('change', function() {
                                $('input.check-warehouse').not(this).prop('checked', false);  
                            });
                        });
                        d.set_primary_action("Select", function() {
                            $(d.body).find('input:checked').each(function(i, input) {
							frappe.model.set_value(item_row.doctype, item_row.name, 'warehouse', $(input).attr('data-warehouse'));
                            });
                            cur_frm.rec_dialog.hide();
                            cur_frm.refresh_fields();
                        });
                        cur_frm.rec_dialog = d;
                        d.show();  
                    }
                }
            });     
    },
    page: this.page,
    description: __('Get Item INFO'),
    ignore_inputs: true,
    
});
27 Likes

Lovely,
One of our client had such a requirement but for last sales price of the item sold to the selected customer instead of item quantity.

Nice work and thanks for sharing the codes.

2 Likes

Good sharing!

1 Like

Good show, works as aadvertised.

Looks like you can use this for any doctype with an item table. I tested it on Purchase Receipt and it works

Regards

1 Like

@youssef

Thanks a million for sharing this!

Its greatly appreciated

Just thought to share the code below which is compatible with Version 11 for anyone that might benefit.

frappe.ui.keys.on('ctrl+q', function(e) {
    	 {
    		
    		  { 
                const current_doc = $('.data-row.editable-row').parent().attr("data-name");
                const item_row = locals["Sales Invoice Item"][current_doc];
                frappe.call({
                    method: 'erpnext.stock.dashboard.item_dashboard.get_data',
                    args: {
                        item_code: item_row.item_code,
                    },
                    callback: function(r) {
                        if (r.message.length > 0){
                            const d = new frappe.ui.Dialog({
                                title: __('Item Balance'),
                                width: 400
                            });
                            $(`<div class="modal-body ui-front">
                                <h2>${item_row.item_code}</h2>
                                <p>Choose Warehouse and click Select :</p>
                                <table class="table table-bordered">
                                <thead>
                                    <tr>
                                    <th>Check</th>
                                    <th>Warehouse</th>
                                    <th>Qty</th>
                                    <th>UOM</th>
                                    </tr>
                                </thead>
                                <tbody>
                                </tbody>
                                </table>
                            </div>`).appendTo(d.body);
                            r.message.forEach(element => {
                                const tbody = $(d.body).find('tbody');
                                const tr = $(`
                                <tr>
                                    <td><input type="checkbox" class="check-warehouse" data-warehouse="${element.warehouse}"></td>
                                    <td>${element.warehouse}</td>
                                    <td>${element.actual_qty}</td>
                                    <td>${item_row.stock_uom }</td>
                                </tr>
                                `).appendTo(tbody)
                                tbody.find('.check-warehouse').on('change', function() {
                                    $('input.check-warehouse').not(this).prop('checked', false);  
                                });
                            });
                            d.set_primary_action("Select", function() {
                                $(d.body).find('input:checked').each(function(i, input) {
    							frappe.model.set_value(item_row.doctype, item_row.name, 'warehouse', $(input).attr('data-warehouse'));
                                });
                                cur_frm.rec_dialog.hide();
                                cur_frm.refresh_fields();
                            });
                            cur_frm.rec_dialog = d;
                            d.show();  
                        }
                    }
                });     
        }
    		
    	}
    });
4 Likes

I think this what you need

2 Likes

Dear Bro ,

Would you let me know for which file I have to edit and add this Script ?

Many Thanks

Yassin

Search for “Custom Script” in the awesomebar. Create new script and select Sales Invoices in the document type.

1 Like

You may need to change this line for other doctypes.

const item_row = locals["Sales Invoice Item"][current_doc];
1 Like

many many thanks Bro , great Job

many thanks for sharing @youssef

1 Like

This might oot from the op but this helped me:

erpnext.stock.doctype.stock_reconciliation.stock_reconciliation.get_items
This will returns all your items with actual qty to date, but for now with no pagination nor filters added…

Thank you…

السلام عليكم
I got this message with I use the code below
App csf_tz is not installed
Failed to get method for command csf_tz.custom_api.get_item_prices with App csf_tz is not installed

could you help.
thanks

frappe.ui.keys.add_shortcut({
shortcut: ‘ctrl+u’,
action: () => {
const current_doc = $(‘.data-row.editable-row’).parent().attr(“data-name”);
const item_row = locals[“Sales Invoice Item”][current_doc];
frappe.call({
method: ‘csf_tz.custom_api.get_item_prices’,
args: {
item_code: item_row.item_code,
currency: cur_frm.doc.currency,
company: cur_frm.doc.company
},
callback: function(r) {
if (r.message.length > 0){
const e = new frappe.ui.Dialog({
title: __(‘Item Prices’),
width: 600
});
$(<div class="modal-body ui-front"> <h2>${item_row.item_code} : ${item_row.qty}</h2> <p>Choose Price and click Select :</p> <table class="table table-bordered"> <thead> </thead> <tbody> </tbody> </table> </div>).appendTo(e.body);
const thead = $(e.body).find(‘thead’);
$(<tr> <th>Check</th> <th>Rate</th> <th>Qty</th> <th>Date</th> <th>Invoice</th> <th>Customer</th> </tr>).appendTo(thead);
r.message.forEach(element => {
const tbody = $(e.body).find(‘tbody’);
const tr = $( <tr> <td><input type="checkbox" class="check-rate" data-rate="${element.price}"></td> <td>${element.price}</td> <td>${element.qty}</td> <td>${element.date }</td> <td>${element.invoice }</td> <td>${element.customer }</td> </tr> ).appendTo(tbody);

                        tbody.find('.check-rate').on('change', function() {
                            $('input.check-rate').not(this).prop('checked', false);  
                        });
                    });
                    e.set_primary_action("Select", function() {
                        $(e.body).find('input:checked').each(function(i, input) {
                            frappe.model.set_value(item_row.doctype, item_row.name, 'rate', $(input).attr('data-rate'));
                        });
                        cur_frm.rec_dialog.hide();
                        cur_frm.refresh_fields();
                    });
                    cur_frm.rec_dialog = e;
                    e.show();  
                }
                else {
                    frappe.show_alert({message:__('There is No Records'), indicator:'red'}, 5);
                }
            }
        });     
},
page: this.page,
description: __('Select Item Price'),
ignore_inputs: true,

});