Calculate Commission on Sales Orders

This customization calculates a custom commission amount when a sales order is saved. The calculated commission amount is displayed on a custom field on the sales order record. The commission is based on the sum of commission amounts for each item included in the sales order: the MSRP of the item, the net distributor costs (MSRP * 0.5), and the price of the item sold to the customer.

While NetSuite includes an Employee Commissions feature, this customization calculates and sets a simple custom commission amount in a custom field without using the feature. For more information about the more robust Employee Commissions feature, which can calculate commission for employees with different schedules and methods of calculation, see Commissions.

This project is available on the SuiteCloud Project Repository on Oracle Samples GitHub and include complete project customization setup with custom objects, unit tests, package file configuration, and more.

Customization Details

The customization for this use case includes:

  • A custom field (MSRP) to store the MSRP for an item

  • A custom field (Custom Commission) to store the calculated commission

  • A user event script triggered on the afterSubmit entry point

Steps in this tutorial to complete this customization:

Before You Begin

The following table lists features, permissions, and other requirements necessary for performing this tutorial and implementing the solution:

Required Features

The following features must be enabled in your account:

  • Server SuiteScript - This feature allows you to attach server scripts to records.

  • File Cabinet - This feature allows you to store your script files in the NetSuite File Cabinet.

  • A/R - This feature allows you to create, edit, and save invoices.

  • Sales Order - This feature allows you to create, edit, and save sales orders (before fulfillment or payment).

For more information, see Enabling Features.

Required Permissions

You will need a role with access to the following:

  • Scripts – Edit access

  • Script Deployments – Edit access

  • Transaction Body Fields – Create access

  • Transaction Line Fields – Create access

  • Sales Orders – Edit and Delete access

For more information, see NetSuite Permissions Overview.

Other Requirements

None

Step 1: Create the Custom Fields

This customization uses two custom fields. The Calculated Commission field stores the calculated commission when the sales order is saved. It is a transaction body field added to the sales order record. The MSRP field is displayed on the Items sublist of a sales order and stores the MSRP value for an item on the sales order. It is a transaction line field added to the sales order record.

To create the custom Calculated Commission field:

  1. Go to Customization > Lists, Records, Fields > Transaction Body Fields > New and enter the following values:

    Field

    Value

    Label

    Calculated Commission

    ID

    _commission_amount

    Type

    Currency

    Store Value

    Checked

    Applies To

    Sale

    Display

    Subtab: Main

  2. Click Save.

To create the custom MSRP field:

  1. Go to Customization > Lists, Records, Fields > Transaction Line Fields > New and enter the following values:

    Field

    Value

    Label

    MSRP

    ID

    _salesorder_msrp

    Type

    Currency

    Store Value

    Checked

    Applies To

    Sale Item

  2. Click Save.

For more information about creating custom components, see the following help topics:

Step 2: Write the Script

This script calculates a custom commission amount when a sales order is saved. The calculated commission amount is displayed on a custom field on the sales order record. The commission is based on the sum of commission amounts for each item included in the sales order: the MSRP of the item, the net distributor costs (MSRP * 0.5), and the price of the item sold to the customer.

Script Summary

The following table summarizes the script:

Script: Calculate Commission

Script Type

SuiteScript 2.x User Event Script Type

Modules Used

  • N/record Module

  • N/log Module - This module is available to all scripts as a global object. However, you should explicitly load this module to avoid conflicts with other objects that may be named ‘log’.

Entry Points

For more information about script types and entry points, see SuiteScript 2.x Script Types.

The Complete Script

This tutorial includes the complete script along with individual steps you can follow to build the script in logical sections. The complete script is provided below so that you can copy and paste it into your text editor and save the script as a .js file (for example, ue_calculateCommission.js).

If you would rather create your script by adding code in logical sections, follow the steps in Build the Script.

Note:

This tutorial script uses the define function, which is required for an entry point script (a script you attach to a script record and deploy). You must use the require function if you want to copy the script into the SuiteScript Debugger and test it. For more information, see SuiteScript Debugger.

Important:

This sample uses SuiteScript 2.1. For more information, see SuiteScript 2.1.

              /**
 * @NApiVersion 2.1
 * @NScriptType UserEventScript
 * @NModuleScope SameAccount
 */

define(['N/record', 'N/log'], (record, log) => {
    function afterSubmit(scriptContext) {
        const stMethodName = 'afterSubmit_calculateCommission';
        try {
            if (scriptContext.type !== scriptContext.UserEventType.CREATE && scriptContext.type !== scriptContext.UserEventType.EDIT) {
                return;
            }
            let stItemType = null;
            let flMSRPTotalAmt = 0.00;
            let flMSRPAmt = 0.00;
            let flNetDistributorCost = 0.00;
            let flCommissionAmount = null;
            let flQuantity = 0.00;
            const recSalesOrder = scriptContext.newRecord;
            let flSubtotal = parseFloat(recSalesOrder.getValue({
                fieldId: 'subtotal'
            }));
            const numItems = recSalesOrder.getLineCount({
                sublistId: 'item'
            });
            for (let intLinenum = 0; intLinenum < numItems; intLinenum++) {
                flMSRPAmt = parseFloat(recSalesOrder.getSublistValue({
                    sublistId: 'item',
                    fieldId: 'custcol_salesorder_msrp',
                    line: intLinenum
                }));
                flQuantity = parseFloat(recSalesOrder.getSublistValue({
                    sublistId: 'item',
                    fieldId: 'quantity',
                    line: intLinenum
                }));
                stItemType = recSalesOrder.getSublistValue({
                    sublistId: 'item',
                    fieldId: 'itemtype',
                    line: intLinenum
                });
                if (stItemType !== 'Discount' && stItemType !== 'Subtotal' && stItemType !== 'Markup') {
                    flMSRPTotalAmt = flMSRPTotalAmt + (flMSRPAmt * flQuantity);
                }
            }
            flNetDistributorCost = flMSRPTotalAmt * 0.5;
            if (flSubtotal === flNetDistributorCost) {
                flCommissionAmount = flSubtotal * 0.10;
            } else if ((flSubtotal > flNetDistributorCost) && (flSubtotal <= flMSRPTotalAmt)) {
                flCommissionAmount = flNetDistributorCost * 0.10 + (flSubtotal - flNetDistributorCost) * 0.75;
            } else {
                if (flSubtotal > flMSRPTotalAmt) {
                    flCommissionAmount = flNetDistributorCost * 0.10 + (flMSRPTotalAmt - flNetDistributorCost) * 0.75 + (flSubtotal - flMSRPTotalAmt) * 0.5;
                }
            }
            const thisSalesOrderID = recSalesOrder.id;
            const updateSalesOrder = record.load({
                type: record.Type.SALES_ORDER,
                id: thisSalesOrderID
            });
            updateSalesOrder.setValue({
                fieldId: 'custbody_commission_amount',
                value: flCommissionAmount
            });
            const updateSalesOrderID = updateSalesOrder.save();
        } catch(e) {
            log.debug({
                title: stMethodName,
                details: ' - Exit (Catch)- '
            });
            if (e.getDetails !== undefined) {
                log.error({
                    title: 'Process Error',
                    details: JSON.stringify(e)
                });
                throw e;
            } else {
                log.error({
                    title: 'Unexpected Error',
                    details: JSON.stringify(e)
                });
                throw error.create({
                    name: 'Unexpected Error',
                    message: JSON.stringify(e)
                });
            }
        }
    }
    return {
        afterSubmit: afterSubmit
    };
}); 

            

Build the Script

You can write the script using a step-by-step approach that includes the following:

Note:

The code snippets included below do not account for indentation. Refer to The Complete Script for suggested indentation.

Start with required opening lines

JSDoc comments and a define function are required at the top of the script file. The JSDoc comments in this script indicate that it is a SuiteScript 2.x (2.0 or 2.1) user event script. The script uses two SuiteScript modules specified in the define statement:

  • N/record – provides access to the sales order record

  • N/log – allows you to log execution details

Start a new script file using any text editor and place the following JSDoc comments and define function at the top of the file:

Note:

This tutorial script uses the define function, which is required for an entry point script (a script you attach to a script record and deploy). You must use the require function if you want to copy the script into the SuiteScript Debugger and test it. For more information, see SuiteScript Debugger.

                /**
 * @NApiVersion 2.1
 * @NScriptType UserEventScript
 * @NModuleScope SameAccount
 */

define(['N/record', 'N/log'], (record, log) => {
}); 

              

Create the entry point function

This script is triggered on the afterSubmit entry point when a sales order is saved. A try-catch block is used to log any errors that might occur during script execution. Most of the script code will be placed in the try block.

Add the following function definition and initial try-catch block statements at the top of the define function:

                function afterSubmit(scriptContext) {
    const stMethodName = 'afterSubmit_calculateCommission';
    try {
    } catch(e) {
        log.debug({
            title: stMethodName,
            details: ' - Exit (Catch)- '
        });
        if (e.getDetails !== undefined) {
            log.error({
                title: 'Process Error',
                details: JSON.stringify(e)
            });
            throw e;
        } else {
            log.error({
                title: 'Unexpected Error',
                details: JSON.stringify(e)
            });
            throw error.create({
                name: 'Unexpected Error',
                message: JSON.stringify(e)
            });
        }
    }
} 

              

Check the scriptContext

You want this script to only execute when the user creates or edits a sales order.

Add the following code at the top of the try block:

                if (scriptContext.type !== scriptContext.UserEventType.CREATE && scriptContext.type !== scriptContext.UserEventType.EDIT) {
    return;
} 

              

Initialize variables

This script uses several variables to store the type, quantity, MSRP, net distributor cost for each item, the sum of all MSRP amounts, and the calculated commission amount. These variables need to be initialized.

Add the following code within the try block:

                let stItemType = null;
let flMSRPTotalAmt = 0.00;
let flMSRPAmt = 0.00;
let flNetDistributorCost = 0.00;
let flCommissionAmount = null;
let flQuantity = 0.00; 

              

Get the subtotal from the sales order record

You want to get the subtotal from the record for use in calculating the commission.

Add the following code within the try block:

                const recSalesOrder = scriptContext.newRecord;
let flSubtotal = parseFloat(recSalesOrder.getValue({
    fieldId: 'subtotal'
})); 

              

Process items on the sales order

Each item on the sales order needs to be individually processed by first getting the MSRP, quantity, and item type for the item, and then checking to make sure the item is not a discount, markup or subtotal item (those types of items are not used to calculate the commission). To process the items, you will add code for the following:

To begin, add the following code within the try block:

                const numItems = recSalesOrder.getLineCount({
    sublistId: 'item'
});
for (let intLinenum = 0; intLinenum < numItems; intLinenum++) {
} 

              
Get the item MSRPs

Now get the MSRP for each item.

Add the following code as the first statement inside the for block:

                  flMSRPAmt = parseFloat(recSalesOrder.getSublistValue({
    sublistId: 'item',
    fieldId: 'custcol_salesorder_msrp',
    line: intLinenum
})); 

                
Get the item quantity and type

And get the item quantity and the item type.

Add the following code within the for block:

                  flQuantity = parseFloat(recSalesOrder.getSublistValue({
    sublistId: 'item',
    fieldId: 'quantity',
    line: intLinenum
}));
stItemType = recSalesOrder.getSublistValue({
    sublistId: 'item',
    fieldId: 'itemtype',
    line: intLinenum
}); 

                
Calculate the MSRP for all items that are not discount, markup, or subtotal items

This script sums all item MSRPs to calculate the total MSRP. MSRP values for items that are discount, markup, or subtotal items are not included.

Add the following code within the for block:

                  if (stItemType !== 'Discount' && stItemType !== 'Subtotal' && stItemType !== 'Markup') {
    flMSRPTotalAmt = flMSRPTotalAmt + (flMSRPAmt * flQuantity);
} 

                

Calculate the net distributor cost

Now calculate the net distributor cost. This cost is used in the calculation of the commission.

Add the following code after the end of the for block within the try block:

                flNetDistributorCost = flMSRPTotalAmt * 0.5; 

              

Calculate the commission amount

The commission amount for the entire sales order needs to be calculated. The commission is based on the sum of commission amounts for each item included in the sales order: the MSRP of the item, the net distributor costs (MSRP * 0.1), and the price of the item sold to the customer.

Add the following code within the try block:

                if (flSubtotal === flNetDistributorCost) {
    flCommissionAmount = flSubtotal * 0.10;
} else if ((flSubtotal > flNetDistributorCost) && (flSubtotal <= flMSRPTotalAmt)) {
    flCommissionAmount = flNetDistributorCost * 0.10 + (flSubtotal - flNetDistributorCost) * 0.75;
} else {
    if (flSubtotal > flMSRPTotalAmt) {
        flCommissionAmount = flNetDistributorCost * 0.10 + (flMSRPTotalAmt - flNetDistributorCost) * 0.75 + (flSubtotal - flMSRPTotalAmt) * 0.5;
    }
} 

              

Set the commission amount on the sales order

Finally, set the final commission amount on the custom Commission Amount field on the header of the sales order record.

Add the following code within the try block:

                const thisSalesOrderID = recSalesOrder.id;
const updateSalesOrder = record.load({
    type: record.Type.SALES_ORDER,
    id: thisSalesOrderID
});
updateSalesOrder.setValue({
    fieldId: 'custbody_commission_amount',
    value: flCommissionAmount
}); 

              

Save the sales order

After the commission amount has been set you want to save the sales order.

Add the following code within the try block:

                const updateSalesOrderID = updateSalesOrder.save(); 

              

Create the return statement

This script associates the afterSubmit function with the afterSubmit user event script entry point.

Add the following code immediately above the closing }); in your script:

                return {
    afterSubmit: afterSubmit
}; 

              

Save your script file

You need to save your script file so you can load it to the NetSuite File Cabinet. Before you save your script file, you may want to adjust the indentation so that the script is readable. Refer to The Complete Script for suggested indentation.

When you are happy with how your script file reads, save it as a .js file (for example, ue_calculateCommission.js).

Step 3: Create the Script Record

Now that you’ve completed the script, you can upload the script file to the File Cabinet and create a script record for it.

For more information about creating script records, see Creating a Script Record.

To create the script record:

  1. Upload your script to the NetSuite File Cabinet.

  2. Go to Customization > Scripting > Scripts > New.

  3. Select your script from the Script File list and click Create Script Record. The Script page is displayed.

  4. On the Script page, enter the following values:

    Field

    Value

    Name

    Calculate Commission

    ID

    _ue_calculate_commission

    NetSuite prepends ‘customscript’ to this ID.

    Description

    This script calculates a custom commission based on each item in a sales order.

  5. Optionally set any other fields on the script record as desired.

  6. Click Save and Deploy. The Script Deployment page appears. Continue with Step 4: Deploy the Script.

Step 4: Deploy the Script

After you create the script record for your script, you can create a script deployment record for it. A script deployment record determines how, when, and for whom the script runs.

For more information about script deployment records, see Script Deployment.

To deploy the script:

  1. Complete the steps in Step 3: Create the Script Record.

  2. On the Script Deployment page, enter the following values:

    Field

    Value

    Applies To

    Sales Order

    ID

    _ue_calculate_commission

    NetSuite prepends 'custdeploy' to this ID.

    Status

    Testing

    The Testing status allows the script owner to test the script without affecting other users in the account.

    Log Level

    Debug

    The Debug level will write all log.debug statements in the script to the Execution Log tab of the script deployment record as well as all errors.

    Audience > Roles

    Check Select All

  3. Click Save.

Step 5: Test the Solution

After you create the script record and deploy your script, you can test your solution by creating a new sales order that includes several items.

To test the solution:

  1. Go to Transaction > Sales > Enter Sales Orders to create a new sales order.

  2. In the Custom Form field, select the Standard Sales Order form. The custom fields will be displayed on all standard and custom sales order forms, but for this tutorial you can use the standard form.

  3. Add multiple items to the sales order, set a value in the MSRP field for each item.

  4. Fill out all other required fields.

  5. Save the sales order.

  6. Review the sales order and verify the calculated commission in the Calculated Commission field.

Related Topics

General Notices