Auto invoice generation On IF creation using user event script

When Item fulfillment is created and in shipped status, an invoice will be created for that item fulfillment.

When an item fulfillment is deleted, the auto-created item fulfillment will also be deleted from Netsuite.

This auto invoice generation script has considered all the item types like Group, inventory, non-inventory, kit, discount, markup, and subtotal.

This script is specifically designed to work in specific conditions.

  • The sales order must be created from Shopify.
  • The sales order must be created from 2021.
/**
 * @NApiVersion 2.x
 * @NScriptType UserEventScript
 */
/****************************************************************************
 * Brooklyn Bicycle Co. |BROOK-52 | Order Process
 * **************************************************************************
 * Date: 13-12-2020
 * 
 * Author: Jobin & Jismi IT Services LLP
 * 
 * Description: 
 * Automated creation of invoice for the itemfulfilled
 * on the sale sorder record. 
 * 
 * Revision  History:
 * Revision 1.0 ${13-12-2020} Gloria : created
 * Revision 1.0 ${08-02-2021} Gloria : Updated to resolve the issue with invoice creation.
 *
 *
 ******************************************************************************/
define(['N/search', 'N/record', 'N/runtime', 'N/format'],
    function (search, record, runtime, format) {

        function beforeSubmit(scriptContext) {
            try {
                //get the current year
                var currrentDate = new Date();
                var year = currrentDate.getFullYear();

                log.debug("scriptContext.type", scriptContext.type)
                var newRec = scriptContext.newRecord;
                var oldRec = scriptContext.oldRecord;


                // when user delete the item fulfillment
                if (scriptContext.type == "delete") {
                    var SoId = oldRec.getValue({ fieldId: "createdfrom" });

                    // related invoice for this itemfulfillment
                    var invId = oldRec.getValue({ fieldId: "custbody_jj_invoice_link" });
                    log.debug("invId", invId)
                    if (SoId) {
                        var salesOrderResult = SalesOrderSearch(SoId)
                        var created = salesOrderResult.created;
                        log.debug('created', created);
                        var Createddate = format.parse({ value: created, type: format.Type.DATETIME, timezone: format.Timezone.AMERICA_NEW_YORK })
                        log.debug('Createddate', Createddate);
                        log.debug('Createddate year', Createddate.getFullYear());
                        var createdYear = Createddate.getFullYear();
                        var type = salesOrderResult.type
                        if (type == "Sales Order" && createdYear >= 2021) {
                            var source = salesOrderResult.source
                            var farApp = salesOrderResult.farApp
                            var webOrdId = salesOrderResult.webOrdId
                            log.debug("source farApp  webOrdId", source + " " + farApp + " " + webOrdId)

                            //when the year is greater than or equal to 2021, source is Web Services,FARAPP MARKETPLACE/CART’ field value is ‘Shopify’
                            // and the ‘FARAPP MARKETPLACE/CART ORDER NUMBER’ field is not empty.
                            if (source == "webServices" && webOrdId && farApp == "Shopify") {
                                log.debug("invId del", invId)
                                if (invId) {
                                    //fetching the internal ids of related deposit applications
                                    var depAppl = depositApplication(invId);

                                    if (depAppl[0].length > 0) {

                                        //delete related deposit application of the invoice
                                        for (var i = 0; i < depAppl[0].length; i++) {

                                            var depApplDel = record.delete({
                                                type: record.Type.DEPOSIT_APPLICATION,
                                                id: depAppl[0][i]
                                            });
                                            // log.debug('depApplDel', depApplDel)
                                        }
                                    }
                                    try {
                                        var custDep = getCustomerDeposits(depAppl[0])
                                        log.debug('custDep', custDep)
                                        if (custDep && custDep.length)
                                            for (var i = 0; i < custDep.length; i++) {
                                                //customer deposit search for finding the total applied amount.
                                                var appliedAmount = getAppliedAmount(custDep[i])
                                                log.debug('appliedAmount', appliedAmount)
                                                if (appliedAmount && appliedAmount != '0') {
                                                    //set the total applied amount in the customer deposit
                                                    var id = record.submitFields({
                                                        type: record.Type.CUSTOMER_DEPOSIT,
                                                        id: custDep[i],
                                                        values: {
                                                            custbody_jj_applied_amount: appliedAmount
                                                        },
                                                        options: {
                                                            enableSourcing: false,
                                                            ignoreMandatoryFields: true
                                                        }
                                                    });
                                                    log.debug('id', id)
                                                }
                                            }
                                    } catch (e) {
                                        log.error("error@settting AppliedAmount", e)
                                    }

                                    //delete invoice 
                                    var invoiceDel = record.delete({
                                        type: record.Type.INVOICE,
                                        id: invId
                                    });
                                    log.debug('invoiceDel', invoiceDel)
                                }
                            }
                        }
                    }
                }

            } catch (e) {
                log.error("error@beforeSubmit", e)
            }
        }
        function afterSubmit(scriptContext) {
            try {

                //get the current year
                var currrentDate = new Date();
                var year = currrentDate.getFullYear();

                log.debug("scriptContext.type", scriptContext.type)
                var newRec = scriptContext.newRecord;
                var oldRec = scriptContext.oldRecord;
                var SoId = newRec.getValue({ fieldId: "createdfrom" });
                var invId = newRec.getValue({ fieldId: "custbody_jj_invoice_link" });
                log.debug("SoId", SoId)
                if (SoId && !invId) {
                    //status of the item fulfillment
                    var newStatus = scriptContext.newRecord.getValue({ fieldId: 'shipstatus' });
                    log.debug("newStatus", newStatus)

                    var oldStatus = scriptContext.type == "create" ? '' : scriptContext.oldRecord.getValue({ fieldId: 'shipstatus' })
                    log.debug("oldStatus", oldStatus)



                    // when user create the item fulfillment
                    if ((scriptContext.type == "create" && newStatus == "C") || (scriptContext.type == "edit" && (oldStatus != "C" && newStatus == "C")) || scriptContext.type == "ship") {

                        var salesOrderResult = SalesOrderSearch(SoId)
                        var created = salesOrderResult.created;
                        log.debug('created', created);
                        var Createddate = format.parse({ value: created, type: format.Type.DATETIME, timezone: format.Timezone.AMERICA_NEW_YORK })
                        log.debug('Createddate', String(Createddate));
                        log.debug('Createddate year', Createddate.getFullYear());
                        var createdYear = Createddate.getFullYear();
                        var type = salesOrderResult.type
                        if (type == "Sales Order" && createdYear >= 2021) {
                            var source = salesOrderResult.source
                            var farApp = salesOrderResult.farApp
                            var webOrdId = salesOrderResult.webOrdId
                            log.debug("source farApp  webOrdId", source + " " + farApp + " " + webOrdId)

                            //when the source is Web Services,FARAPP MARKETPLACE/CART’ field value is ‘Shopify’
                            // and the ‘FARAPP MARKETPLACE/CART ORDER NUMBER’ field is not be empty.
                            if (source == "webServices" && webOrdId && farApp == "Shopify") {
                                // when user edit the item fulfillment
                                createInvoice(SoId, newRec)
                            }

                        }


                    }
                }
            } catch (e) {
                log.error("error@afterSubmit", e)
            }
        }

        function getCustomerDeposits(depoAppl) {
            var custDepo = [];
            var customerdepositSearchObj = search.create({
                type: "customerdeposit",
                filters:
                    [
                        ["type", "anyof", "CustDep"],
                        "AND",
                        ["applyingtransaction.internalid", "anyof", depoAppl]
                    ],
                columns:
                    [
                        search.createColumn({ name: "internalid", label: "Internal ID" })
                    ]
            });
            var searchResultCount = customerdepositSearchObj.runPaged().count;
            log.debug("customerdepositSearchObj result count", searchResultCount);
            customerdepositSearchObj.run().each(function (result) {
                // .run().each has a limit of 4,000 results
                custDepo.push(result.getValue({ name: "internalid", label: "Internal ID" }))
                return true;
            });
            return custDepo;
        }

        /**
         * customer deposit search for finding the total applied amount.
         * @param {string/number} id - internal id of customer deposit
         * @returns {number}
         */
        function getAppliedAmount(id) {
            var appliedAmount = 0;
            var customerdepositSearchObj = search.create({
                type: "customerdeposit",
                filters:
                    [
                        ["type", "anyof", "CustDep"],
                        "AND",
                        ["internalid", "anyof", id]
                    ],
                columns:
                    [
                        search.createColumn({
                            name: "amountpaid",
                            summary: "SUM",
                            label: "Amount Paid"
                        })
                    ]
            });
            var searchResultCount = customerdepositSearchObj.runPaged().count;
            log.debug("customerdepositSearchObj result count", searchResultCount);
            customerdepositSearchObj.run().each(function (result) {
                // .run().each has a limit of 4,000 results
                appliedAmount = result.getValue({
                    name: "amountpaid",
                    summary: "SUM",
                    label: "Amount Paid"
                });
                // return true;
            });
            return appliedAmount;
        }

        function getSublist(recordObj, sublistId) {
            /**
             * Get Sublist fields and their values
             * 
             * @param recordObj {Object} Initialised Object reference to record
             * @param sublistId {String} sublistId on the Object
             * @return {Array[Object]} 
             */
            var result = [];
            var sublistFields = recordObj.getSublistFields(sublistId);
            var lineCount = recordObj.getLineCount({
                sublistId: sublistId
            });
            for (var line = 0; line < lineCount; line++) {
                result.push(
                    sublistFields.reduce(function (a, c) {
                        try {
                            a[c] = {
                                value: recordObj.getSublistValue({
                                    sublistId: sublistId,
                                    fieldId: c,
                                    line: line
                                }),
                                text: recordObj.getSublistText({
                                    sublistId: sublistId,
                                    fieldId: c,
                                    line: line
                                })
                            };
                            return a;
                        } catch (er) { }
                        return a;
                    }, {})
                );
            }
            return result;
        }

        /**
         * Check whether the item is a related item
         *
         * @param dataArray {Array.<Object>} remaing Sales Order line Items
         * @param index {String|Number} 
         * @param ACCUMULATOR
         * @returns array
         */
        function isRelatedItem(dataArray, i, ACCUMULATOR) {

            const RELATED_ITEM_TYPE = {
                "Markup": "Markup",
                "Discount": "Discount"
            };
            for (var index = i, len = dataArray.length; index < len; index++) {
                if ((!(dataArray[index].isclosed.value)) && RELATED_ITEM_TYPE[dataArray[index].itemtype.value])
                    ACCUMULATOR.push(dataArray[index])
                else //If item is not a releated item
                    break;
            }
            return ACCUMULATOR;
        }

        function lineMap(IF_LINES, SO_LINES) {
            /**
             * Map Item FulFillment Item Lines To Sales Order Item Lines and attach all the related items such as MarkUp,Discount,.. etc
             * 
             * @param IF_LINES{Array[Object]} Item Fulfillment Line Items
             * @param SO_LINES {Array[Object]} Sales Order Line Items
             * @return {Array[Object]}
             */
            const RELATED_ITEM_TYPE = {
                "Markup": "Markup",
                "Discount": "Discount"
            };

            //Map items between Item Fulfillment and Sales Order, contains only Item FulFillment items
            var mappedItems = IF_LINES.map(function (currentValue) {
                log.debug("currentValue", currentValue)
                return Object.assign({}, currentValue,
                    SO_LINES.find(function (eachValue) {
                        return currentValue['orderline'].value == eachValue['line'].value;
                    }) || {}
                );
            });

            //Add all the related items to Mapped Items, contains items in Item Fulfillment and their related items
            var groupedItems = mappedItems.reduce(function (ACCUMULATOR, CURRENT_VALUE, idx, src) {
                ACCUMULATOR.push(CURRENT_VALUE);

                var remainingItems = SO_LINES.filter(function (eachValue) { //Contains all the succeding items in Sales Order from the current item
                    if (parseInt(eachValue.index) > parseInt(CURRENT_VALUE.index))
                        return true;
                    return false;
                });
                log.debug('remainingItems', remainingItems)
                remainingItems.sort(function (a, b) { return a.index - b.index });
                log.debug('remainingItems sorted', remainingItems)
                //Map only the related item in Sales Order to the current item in Item Fulfillment
                for (var index = 0, len = remainingItems.length; index < len; index++) {
                    if ((!remainingItems[index].isclosed.value) && remainingItems[index].index >= CURRENT_VALUE.index + 1 && RELATED_ITEM_TYPE[remainingItems[index].itemtype.value]) //Process only RELATED_ITEM_TYPE such as Markup,Discount
                        ACCUMULATOR.push(remainingItems[index]);
                    else if (RELATED_ITEM_TYPE[remainingItems[index].itemtype.value])
                        continue;
                    else if (remainingItems[index].itemtype.value == 'Group')
                        break;
                    else if (remainingItems[index].itemtype.value == 'EndGroup') //If the current Item is a EndGroup item
                        continue;
                    else if (remainingItems[index].ingroup.value == "T" && remainingItems[index].itemtype.value != 'Group') //If the current Item is a Group Item or EndGroup item and not starting line of group.
                        continue;
                    else
                        break;
                }
                log.debug("ACCUMULATOR1", ACCUMULATOR)
                for (var index = 0, len = remainingItems.length; index < len; index++) {

                    if ((!remainingItems[index].isclosed.value) && remainingItems[index].itemtype.value == 'Subtotal') { //Process only RELATED_ITEM_TYPE such as Subtotal
                        ACCUMULATOR.push(remainingItems[index]);
                        if ((!remainingItems[index].isclosed.value) && remainingItems[index + 1] && RELATED_ITEM_TYPE[remainingItems[index + 1].itemtype.value])
                            isRelatedItem(remainingItems, index + 1, ACCUMULATOR)
                        // ACCUMULATOR.push(remainingItems[index + 1]);
                    }
                    else
                        continue;
                }
                log.debug("ACCUMULATOR2", ACCUMULATOR)
                return ACCUMULATOR;
            }, []);
            log.debug("groupedItems", groupedItems)
            return groupedItems;
        }

        // create invoice for the item fulfillement
        function createInvoice(SoId, newRec) {
            /**
             * Create an invoice when the item fulfillment is shipped.
             * @param SoId {Number} sales order id
             * @param newRec {Object} Item fulfillment object
             * @return no return value
             */
            var orderline = []; var quantity = {}
            var SO_RECORD = record.load({
                type: record.Type.SALES_ORDER,
                id: SoId,
                isDynamic: true
            });
            var SOLines = getSublist(SO_RECORD, 'item').reduce(function (a, v, idx) {
                a.push({
                    item: v.item,
                    itemtype: v.itemtype,
                    groupsetup: v.groupsetup,
                    ingroup: v.ingroup,
                    rate: v.rate,
                    description: v.description,
                    line: v.line,
                    isclosed: v.isclosed,
                    index: idx
                });
                return a;
            }, [])

            var IFLines = [];
            var numLines = newRec.getLineCount({
                sublistId: 'item'
            });
            log.debug("numLines", numLines)
            for (var i = 0; i < numLines; i++) {
                //fetch the order lines that are added in the item fulfillement and the quantity on it.
                var fulfill = newRec.getSublistValue({ fieldId: "itemreceive", sublistId: 'item', line: i })
                log.debug('fulfill', fulfill)
                if (fulfill) {
                    var ordLine = newRec.getSublistValue({ fieldId: "orderline", sublistId: 'item', line: i });
                    orderline.push(ordLine);
                    IFLines.push({
                        quantity: { value: newRec.getSublistValue({ fieldId: "quantity", sublistId: 'item', line: i }) },
                        orderline: { value: ordLine },
                        // index: i
                    })

                    quantity[ordLine] = newRec.getSublistValue({ fieldId: "quantity", sublistId: 'item', line: i });
                }

            }
            log.debug('orderline', orderline)
            log.debug('quantity[ordLine]', quantity[ordLine])

            const SKIP_ITEM_TYPE = {
                'Group': 'Group',
                'EndGroup': 'EndGroup',
            };

            var MAP_ARRAY = lineMap(IFLines, SOLines)

            if (MAP_ARRAY.length < 1) //If there is no Mapped Items, returns false
                return false;

            //creating the invoice by transforming the sales order
            var objRecord = record.transform({
                fromType: 'salesorder',
                fromId: SoId,
                toType: 'invoice',
                isDynamic: true,
            });

            var numLines1 = objRecord.getLineCount({
                sublistId: 'item'
            });
            log.debug('numLines1', numLines1)

            //iterating through the item lines of the new invoice object
            for (var i = 0; i < objRecord.getLineCount({
                sublistId: 'item'
            }); i++) {
                if (SKIP_ITEM_TYPE[objRecord.getSublistValue({
                    sublistId: 'item',
                    fieldId: 'itemtype',
                    line: i
                })])
                    continue;
                mappedLine = MAP_ARRAY.find(function (eachValue) { //Filtering Out the Items using MAP_ARRAY
                    return parseInt(eachValue.line.value) == parseInt(objRecord.getSublistValue({
                        sublistId: 'item',
                        fieldId: 'orderline',
                        line: i
                    }));
                }) || false;
                log.debug('mappedLine', mappedLine)
                if (mappedLine) {
                    if (mappedLine.quantity)
                        if (mappedLine.quantity.value) {
                            objRecord = objRecord.selectLine({
                                sublistId: 'item',
                                line: i
                            });
                            objRecord.setCurrentSublistValue({
                                sublistId: 'item',
                                fieldId: 'quantity',
                                value: mappedLine.quantity.value,
                                // ignoreFieldChange: true
                            });
                            objRecord.commitLine({
                                sublistId: 'item'
                            });
                        }
                } else {
                    objRecord.removeLine({
                        sublistId: 'item',
                        line: i,
                        ignoreRecalc: false
                    });
                    i--;

                }
            }

            //remove the item groups that don't have any items in it.
            for (var index = 0; index < objRecord.getLineCount({
                sublistId: 'item'
            }); index++) { // loop through all the line items
                if (objRecord.getSublistValue({
                    sublistId: 'item',
                    fieldId: 'itemtype',
                    line: index
                }) === 'Group') {
                    var ItemType = objRecord.getSublistValue({
                        sublistId: 'item',
                        fieldId: 'itemtype',
                        line: index
                    });
                    var itemTypeOnNextLine = objRecord.getSublistValue({
                        sublistId: 'item',
                        fieldId: 'itemtype',
                        line: index + 1
                    })

                    if (ItemType === 'Group' && itemTypeOnNextLine === 'EndGroup') {
                        // remove Item Group
                        objRecord.removeLine({
                            sublistId: 'item',
                            line: index,
                            ignoreRecalc: false
                        });
                        index--;
                    }

                }
            }
            var recordId = objRecord.save({
                enableSourcing: true,
                ignoreMandatoryFields: true
            });
            log.debug('recordId', recordId)

            //store the invoice id in the item fulfillment in a custom hidden field
            // newRec.setValue({ fieldId: "custbody_jj_invoice_link", value: recordId })
            var id = record.submitFields({
                type: record.Type.ITEM_FULFILLMENT,
                id: newRec.id,
                values: {
                    custbody_jj_invoice_link: recordId
                },
                options: {
                    enableSourcing: false,
                    ignoreMandatoryFields: true
                }
            });
            log.debug('id', id)

        }
        function SalesOrderSearch(id) {
            var SoResult = {};
            var transactionSearchObj = search.create({
                type: "transaction",
                filters:
                    [
                        ["mainline", "is", "T"],
                        "AND",
                        ["internalid", "anyof", id]
                    ],
                columns:
                    [
                        search.createColumn({ name: "type", label: "Type" }),
                        search.createColumn({ name: "source", label: "Source" }),
                        search.createColumn({ name: "custbody_fa_channel", label: "FarApp Marketplace/Cart" }),
                        search.createColumn({ name: "custbody_fa_channel_order", label: "FarApp Marketplace/Cart Order Number" }),
                        search.createColumn({ name: "datecreated", label: "Date Created" })
                    ]
            });
            var searchResultCount = transactionSearchObj.runPaged().count;
            log.debug("transactionSearchObj result count", searchResultCount);
            transactionSearchObj.run().each(function (result) {
                // .run().each has a limit of 4,000 results
                SoResult.type = result.getText({ name: "type", label: "Type" })
                SoResult.source = result.getValue({ name: "source", label: "Source" })
                SoResult.farApp = result.getValue({ name: "custbody_fa_channel", label: "FarApp Marketplace/Cart" })
                SoResult.webOrdId = result.getValue({ name: "custbody_fa_channel_order", label: "FarApp Marketplace/Cart Order Number" })
                SoResult.created = result.getValue({ name: "datecreated", label: "Date Created" })
            });
            return SoResult;
        }
        //finding the all deposite applications related to the invoice
        function depositApplication(id) {
            var depAppl = []; var status;
            var invoiceSearchObj = search.create({
                type: "invoice",
                filters:
                    [
                        ["type", "anyof", "CustInvc"],
                        "AND",
                        ["applyingtransaction.type", "anyof", "DepAppl"],
                        "AND",
                        ["internalid", "anyof", id]
                    ],
                columns:
                    [
                        search.createColumn({
                            name: "internalid",
                            join: "applyingTransaction",
                            label: "Internal ID"
                        }),
                        search.createColumn({ name: "statusref", label: "Status" })
                    ]
            });
            var searchResultCount = invoiceSearchObj.runPaged().count;
            log.debug("invoiceSearchObj result count", searchResultCount);
            invoiceSearchObj.run().each(function (result) {
                // .run().each has a limit of 4,000 results
                depAppl.push(result.getValue({
                    name: "internalid",
                    join: "applyingTransaction",
                    label: "Internal ID"
                }))
                status = result.getValue({ name: "statusref", label: "Status" })
                return true;
            });
            return [depAppl, status];
        }
        return {

            beforeSubmit: beforeSubmit,
            afterSubmit: afterSubmit
        }
    });

if (typeof Object.assign != 'function') {
    Object.defineProperty(Object, "assign", {
        value: function assign(target, varArgs) {
            'use strict';
            if (target == null) {
                throw new TypeError('Cannot convert undefined or null to object');
            }
            var to = Object(target);
            for (var index = 1; index < arguments.length; index++) {
                var nextSource = arguments[index];
                if (nextSource != null) {
                    for (var nextKey in nextSource) {
                        if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                            to[nextKey] = nextSource[nextKey];
                        }
                    }
                }
            }
            return to;
        },
        writable: true,
        configurable: true
    });
}

if (!Array.prototype.find) {
    Object.defineProperty(Array.prototype, 'find', {
        value: function (predicate) {
            if (this == null) {
                throw new TypeError('"this" is null or not defined');
            }
            var o = Object(this);
            var len = o.length >>> 0;
            if (typeof predicate !== 'function') {
                throw new TypeError('predicate must be a function');
            }
            var thisArg = arguments[1];
            var k = 0;
            while (k < len) {
                var kValue = o[k];
                if (predicate.call(thisArg, kValue, k, o)) {
                    return kValue;
                }
                k++;
            }
            return undefined;
        },
        configurable: true,
        writable: true
    });
}









Leave a comment

Your email address will not be published. Required fields are marked *