Currently reviewing the following script and was w...
# suitescript
h
Currently reviewing the following script and was wondering if anyone has recommendations? This is my first complicated suitescript and will eventually impact every order in our system. Any tidbit of information will be helpful before I implement and please feel free to rip into any issues that this code has. Summary: This script runs before a Sales Order is submitted. It checks if the Sales Order has a subtotal of over $500, validates that the customer has not received a catalog in the last year, and then adds catalog items to the Sales Order based on the items being ordered. It then updates the last catalog received date.
Copy code
/**
 * @NApiVersion 2.1
 * @NScriptType UserEventScript
 * @NModuleScope SameAccount
 */

// Load Necessary Modules
define(['N/record', 'N/search'], function(record, search) {

    //Update the last catalog date
    function updateLastCatalog(customerId, today) {
        const customerRecord = record.load({
          type: record.Type.CUSTOMER,
          id: customerId
        });
      
        customerRecord.setValue({
          fieldId: 'custentity_last_order_date',
          value: today
        });
      
        customerRecord.save();
      }
    
    function beforeSubmit(context) {

        //Only run on sales orders being created over $500
        if (context.type === context.UserEventType.CREATE && context.newRecord.type === record.Type.SALES_ORDER ) {

            const newRecord = context.newRecord;


            if (newRecord.getValue({
                    fieldId: 'subtotal'
                }) < 500) {
                return;
            }

            //Validate that this customer hasnt received a catalog in the last year
            const customerId = newRecord.getValue({fieldId: 'entity'});
            const customerInfo = search.lookupFields({
                type: search.Type.CUSTOMER,
                id: customerId,
                columns: ['entityid', 'custentity_last_order_date', 'custentity_catalog_no_send']
            });

            const customerName = customerInfo.entityid;
            const customerLastCatalogString = customerInfo.custentity_last_order_date;
            const customerNoCatalog = customerInfo.custentity_catalog_no_send;

            const today = new Date();
            const customerLastCatalog = new Date(customerLastCatalogString)
            const daysSinceLastCatalog = (customerLastCatalogString == "") ? 366 : Math.round((today - customerLastCatalog) / 86400000);

            if ((customerName === 'Amazon Online Sales' || daysSinceLastCatalog > 365) && !customerNoCatalog) {

            } else {
                return;
            }

            //Go through each item to see what catalogs the order needs
            let uniqueCatalogs = new Set();
            const lineCount = newRecord.getLineCount({
                sublistId: 'item'
            });

            for (let i = 0; i < lineCount; i++) {
                const itemId = newRecord.getSublistValue({
                    sublistId: 'item',
                    fieldId: 'item',
                    line: i
                });

                //If a catalog is already on the order exit script
                if (itemId === 8102 || itemId === 8101 || itemId === 2769 || itemId === 8103) {
                    updateLastCatalog(customerId, today)
                    return;
                }
                const itemCatalog = search.lookupFields({
                    type: search.Type.ITEM,
                    id: itemId,
                    columns: ['custitem_catalog_field']
                }).custitem_catalog_field;

                if (itemCatalog && itemCatalog.length > 0) {
                    uniqueCatalogs.add(itemCatalog[0].text);
                }
            }

            //Exit script if no products have a catalog
            if (uniqueCatalogs.size === 0){
                return;
            }
            const catalogs = Array.from(uniqueCatalogs);
        
            for (let i = 0; i < catalogs.length; i++) {
                const currentLine = lineCount + i;
                let currentCatalog = "";
                let catalogDesc = catalogs[i] + " CATALOG";
                switch (catalogs[i]) {
                    case "BODY SHOP":
                        currentCatalog = 8102;
                        break;
                    case "INDUSTRIAL":
                        currentCatalog = 8101;
                        break;
                    case "PROTECTIVE":
                        currentCatalog = 8103;
                        break;
                    case "NDT":
                        currentCatalog = 2769;
                        break;
                    default:
                        currentCatalog = 2769; //NDT & Other Catalog cannot exist -need to add NDT catalog Item
                        catalogDesc = catalogs[i] + " | Catalog Not On NS Script"
                        break;
                }

                newRecord.setSublistValue({
                    sublistId: 'item',
                    fieldId: 'item',
                    line: currentLine,
                    value: currentCatalog
                });

                newRecord.setSublistValue({
                    sublistId: 'item',
                    fieldId: 'amount',
                    line: currentLine,
                    value: 0
                });

                newRecord.setSublistValue({
                    sublistId: 'item',
                    fieldId: 'quantity',
                    line: currentLine,
                    value: 1
                });

                newRecord.setSublistValue({
                    sublistId: 'item',
                    fieldId: 'description',
                    line: currentLine,
                    value: catalogDesc
                });
            }


            updateLastCatalog(customerId, today)
        }
    }

    return {
        beforeSubmit: beforeSubmit
    };
});
n
Looks great! A couple things i've found that help me a little. • Line 28 - instead of wrapping your entire code in an if statement, ive found its helpful to check all exit conditions and return if they arent met. Its just a little cleaner. example:
Copy code
// instead of doing this 
if (context.type === context.UserEventType.CREATE && context.newRecord.type === record.Type.SALES_ORDER ) {//do code stuff}

// try this 
If (context.type !== context.UserEventType.CREATE || context.newRecord.type !== record.Type.SALES_ORDER) {return;}
// do code stuff
Also line 28 you don't really need to specify to only run on sales order. You just only deploy it on the sales order.
• Line 55 - You have an if statement that doesn't really do anything. It looks like you only care about the else statement. so reverse your if and add it to the exit conditions at the top
Copy code
if (!((customerName === 'Amazon Online Sales' || daysSinceLastCatalog > 365) && !customerNoCatalog)) {
				return;
			}
• Line 73. You hardcoded your item id's which is fine, but you might want to make these parameters on the script. or maybe 1 parameter with a list of items. That way if in the future the items change or you want to add new ones, you can just update the parameter on the deployment instead of having to update the code
• Line 11 - updateLastCatalog() function. Consider using record.submitFields() instead of loading the record and saving it. Just runs a little better and uses less governance.
this 1
• Line 98 - more hardcoded values. Again not terrible but if you can get around it with parameters its more ideal. Maybe a parameter that is a longtext field with an object that you use to store your different values
It looks good though!
❤️ 1
h
@Nathan L Thank you so much! 🙌
b
Copy code
if (context.type === context.UserEventType.CREATE && context.newRecord.type === record.Type.SALES_ORDER ) {
is mostly unnecessary, you want to use the script deployment to filter on both the context type and the record type
Copy code
const customerLastCatalog = new Date(customerLastCatalogString)
puts your code at risk when the user's date format doesnt match what the Date constructor expects, use _*format.parse*_ instead
Copy code
const itemCatalog = search.lookupFields({
                    type: search.Type.ITEM,
                    id: itemId,
                    columns: ['custitem_catalog_field']
                }).custitem_catalog_field;
is unfavorable when there are multiple lines, either loop through the items and do 1 search to get all of their catalog fields
or add a custom transaction column that uses sourcing to put the field on the transaction line so you dont need to look it up (this technique can also be used for your customer lookup)
this 1
👀 1
Copy code
for (let i = 0; i < catalogs.length; i++) {
                const currentLine = lineCount + i;
                let currentCatalog = "";
                let catalogDesc = catalogs[i] + " CATALOG";
                switch (catalogs[i]) {
                    case "BODY SHOP":
                        currentCatalog = 8102;
                        break;
                    case "INDUSTRIAL":
                        currentCatalog = 8101;
                        break;
                    case "PROTECTIVE":
                        currentCatalog = 8103;
                        break;
                    case "NDT":
                        currentCatalog = 2769;
                        break;
                    default:
                        currentCatalog = 2769; //NDT & Other Catalog cannot exist -need to add NDT catalog Item
                        catalogDesc = catalogs[i] + " | Catalog Not On NS Script"
                        break;
                }

                newRecord.setSublistValue({
                    sublistId: 'item',
                    fieldId: 'item',
                    line: currentLine,
                    value: currentCatalog
                });

                newRecord.setSublistValue({
                    sublistId: 'item',
                    fieldId: 'amount',
                    line: currentLine,
                    value: 0
                });

                newRecord.setSublistValue({
                    sublistId: 'item',
                    fieldId: 'quantity',
                    line: currentLine,
                    value: 1
                });

                newRecord.setSublistValue({
                    sublistId: 'item',
                    fieldId: 'description',
                    line: currentLine,
                    value: catalogDesc
                });
            }
this looks a little weird, it replaces the next line with your description? item
im guessing you actually wanted to insert your catalog description and not replace the next line