Selecting Income Account

I wrote the following script. The idea is to select the appropriate income account based on the sales type and item code found in the sales invoice.

Have i missed some thing it is not working Please Help. Thanks

cur_frm.cscript.custom_validate = function(doc) 
{
//valid only for LITP
      if doc.company = "Labora International Trading PLC";
           {
               if doc.sales_type = "Cash Sales";
                {
                   string x = "cash";
                }
               else
                {
                   string x = "credit";
                }
           }
//looping through Sales Invoice Items table
     $.each(wn.model.get("Sales Invoice Items", {parent:doc.name}), function(i, d) 
    {
           string itemStr = d.item_code;
           string itemChar = itemStr.Substring(0,1);
      switch(doc.item_group) 
       {
        case "RE":
            if x = "cash"
                {
                   doc.income_account = "4200-001 Sales - Reagents Cash - LITP";
                }
            else
                {
                   doc.income_account = "4200-001 Sales - Reagents Credit - LITP";
                }
            break;
        case "MS":
            if x = "cash"
                {
                   doc.income_account = "4300-001 Sales-Medical Supplies Cash - LITP";
                }
            else
                {
                   doc.income_account = "4300-001 Sales-Medical Supplies Credit - LITP";
                }
            break;
        default:
            doc.item_code = "Sales - LITP";
       }
        
    });
}

In javascript, if conditions are like this:

if(doc.company == "Labora International Trading PLC")

There are some basic mistakes in terms of syntax, please check those online first.

Thanks Nabin,
I corrected all the syntax errors, but with no result.
I have two questions regarding this.

  1. can i add another script on the same file. i.e there is a already a script on the Sales Invoice_Client, and i need the effect to be on Sales Invoice?
    2 is the script function (cur_frm.cscript.custom_validate = function(doc)) proper function for achieving the desired result.

Hello
Waiting for some suggestions
Thanks

yes, you can add multiple function in sales custom script record.

If you write the logic in custom_validate, income_account will be set on saving of document. If you want to set income_account on_change of item or sales_type, you have to call the function on trigger of those field. For on_change event syntax, check https://frappe.io/docs/guide/form-client-scripting.

In above code, doc.income_account is wrong, it should be d.income_account.
Can you share your final code?

My final code…

cur_frm.cscript.custom_validate = function(doc) 
{
//valid only for LITP
      if doc.company == "Labora International Trading PLC";
           {
               if doc.sales_type == "Cash Sales";
                {
                   string x = "cash";
                }
               else
                {
                   string x = "credit";
                }
           }
//looping through Sales Invoice Items table
     $.each(wn.model.get("Sales Invoice Items", {parent:doc.name}), function(i, d) 
    {
           string itemStr = d.item_code;
           string itemChar = itemStr.Substring(0,1);
      switch(doc.item_group) 
       {
        case "RE":
            if x == "cash"
                {
                   d.income_account = "4200-001 Sales - Reagents Cash - LITP";
                }
            else
                {
                   d.income_account = "4200-001 Sales - Reagents Credit - LITP";
                }
            break;
        case "MS":
            if x == "cash"
                {
                   d.income_account = "4300-001 Sales-Medical Supplies Cash - LITP";
                }
            else
                {
                   d.income_account = "4300-001 Sales-Medical Supplies Credit - LITP";
                }
            break;
        default:
            d.income_account = "Sales - LITP";
       }

    });
}

Please format your code correctly or use gist.github,com or pastebin.com

I had already formatted this code also as the first one , i don’t know how it happens like that. Any way i posted the same code on GitHub gist with proper formatting, is that ok?.

Refresh income_account field after assigning value of income account.

refresh_field('income_account', d.name, 'entries');

Everyting should be in the right place:

  1. The account assignment is on Party Account
  2. Extend Party Account with a Custom Field say customer_income_account
  3. Apply the Income Account over settings in Item (simply because the same product may be locally sold or exported)
  4. For overcoming permission issues and as a general design approach, leave logic to the server side and only do UX operations on clinet scripts. So, I create and api.py for the custom application collecting all customization and developments for the customer we are implementing ERPNext at.
    It is downhill from here:
import frappe

@frappe.whitelist()
def get_customer_income_account(customer, company):
    """
    Fetch the customer income account from the Party Account child table.
    """
    try:
        frappe.logger().info(f"Fetching customer income account for Customer: {customer}, Company: {company}")

        # Fetch the value from the Party Account child table
        customer_income_account = frappe.get_value(
            'Party Account',
            {'parent': customer, 'parenttype': 'Customer', 'company': company},
            'customer_income_account'  # Fetch the customer_income_account field
        )

        frappe.logger().info(f"Fetched customer income account: {customer_income_account}")
        return customer_income_account
    except Exception as e:
        frappe.log_error(f"Error fetching customer income account: {e}")
        return None

Then, client side would be:

frappe.ui.form.on('Sales Invoice', {
    onload: function(frm) {
        // Populate income account for all items when the form is loaded
        frm.doc.items.forEach(function(item) {
            if (item.item_code && frm.doc.company && frm.doc.customer) {
                fetch_customer_income_account(frm, item);
            }
        });
    },
    refresh: function(frm) {
        // Populate income account for all items when the form is refreshed
        frm.doc.items.forEach(function(item) {
            if (item.item_code && frm.doc.company && frm.doc.customer) {
                fetch_customer_income_account(frm, item);
            }
        });
    }
});

frappe.ui.form.on('Sales Invoice Item', {
    item_code: function(frm, cdt, cdn) {
        let item = frappe.get_doc(cdt, cdn);
        if (item.item_code && frm.doc.company && frm.doc.customer) {
            frappe.call({
                method: 'CUSTOM_APP.api.get_customer_income_account', // Updated method path
                args: {
                    'customer': frm.doc.customer,
                    'company': frm.doc.company
                },
                callback: function(r) {
                    if (r.message) {
                        frappe.model.set_value(cdt, cdn, 'income_account', r.message);
                    } else {
                        frappe.msgprint(__("No customer income account found for the selected customer and company."));
                    }
                }
            });
        }
    }
});

function fetch_customer_income_account(frm, item) {
    frappe.call({
        method: 'CUSTOM_APP.api.get_customer_income_account', // Updated method path
        args: {
            'customer': frm.doc.customer,
            'company': frm.doc.company
        },
        callback: function(r) {
            if (r.message) {
                frappe.model.set_value(item.doctype, item.name, 'income_account', r.message);
            } else {
                frappe.msgprint(__("No customer income account found for the selected customer and company."));
            }
        }
    });
}

Don’t bother with jscript side, get it done via AI on Cursor app… IMHO power is at the design and logic embedded in your CUSTOM_APP api…

TO DO: Refactor so that if enabled (make everything available to the customer and parametric), the Sales Invoice should compare the Customer’s Billing Adress.Country with the current Company.Country (or Billing Address.Country set on Company details) and determine Export Income and Local Sales Income Accounts accordingly