Auto-Invoice

The script will create an invoice from the Item Fulfillemrnts for the same items & quantity shipped for a specific user and role. We are using the Sales Order transformation method for achieving this.

/**
 * @NApiVersion 2.1
 * @NScriptType UserEventScript
 */
/*******************************************************************************
 * AMVOP- Consolidate the QR Code Logic with Auto-Invoicing
 * *************************************************************************
 *
 * Created Date: 24-12-2021
 *
 * Author: Jobin & Jismi IT Services LLP
 *
 * Revision  History:
 * Revision 1.0- Consolidated the script with QR code encoder code 07-01-2022
 * Revision 1.1- Updated with changes in QR code encoder code 12-01-2022
 * Revision 1.2- Script rewritten to correct the remove logic 28/01/2022
 *****************************************************************************
 **/

//global variables are initialized below
const invAutomationUserRole = 1068; //Set as "AMVOP Warehouseman" role internal id
let approvalStatus = 1; //1 stands for 'pending approval' and 2 for 'approved'

//This is an updated version "JJ UE Autoinvoice AMVOP-259" with code added to update the custom field for QR code
define( [ 'N/encode', 'N/record', 'N/runtime', 'N/search' ],
    /**
     * @param{encode} encode
     * @param{record} record
     * @param{runtime} runtime
     * @param{search} search
     */
    ( encode, record, runtime, search ) =>
    {
        /**
         * Defines the function definition that is executed before record is loaded.
         * @param {Object} scriptContext
         * @param {Record} scriptContext.newRecord - New record
         * @param {string} scriptContext.type - Trigger type; use values from the context.UserEventType enum
         * @param {Form} scriptContext.form - Current form
         * @param {ServletRequest} scriptContext.request - HTTP request information sent from the browser for a client action only.
         * @since 2015.2
         */
        const afterSubmit = ( scriptContext ) =>
        {
            try
            {
                let tagHex, formattedTime;

                if ( scriptContext.type == scriptContext.UserEventType.DELETE )
                    return true;//terminates code execution returning true value if context is delete

                let orderTypeIs = scriptContext.newRecord.getValue( { fieldId: "ordertype" } );

                if ( orderTypeIs != 'SalesOrd' )
                    return true;//terminates the script execution if order type is not SO

                let temp1, newIFRecord, oldIFRecord, salesOrderId, oldItemFulfillmentStatus, newItemFulfillmentStatus,
                    temp = {};
                var itemList = [];

                temp1 = runtime.getCurrentUser();//get current user
                if ( checkUser( temp1 ) )
                {
                    oldIFRecord = scriptContext.oldRecord;
                    newIFRecord = scriptContext.newRecord;

                    newItemFulfillmentStatus = newIFRecord.getValue( { fieldId: "shipstatus" } );
                    if ( oldIFRecord != null )
                        oldItemFulfillmentStatus = oldIFRecord.getValue( { fieldId: "shipstatus" } );

                    if ( ( newItemFulfillmentStatus != oldItemFulfillmentStatus && newItemFulfillmentStatus == 'C' ) || scriptContext.type == scriptContext.UserEventType.SHIP )
                    {
                        salesOrderId = newIFRecord.getValue( {
                            fieldId: "createdfrom"
                        } );
                        log.debug( "Related sales order id", salesOrderId );

                        //Finding the reference number of item fulfillment
                        let itemFulfillId = newIFRecord.id;
                        log.debug( "Item fulfillment id", itemFulfillId );


                        //creating a invoice record using record.transform() function
                        var newInvoiceRecord = record.transform( {
                            fromType: record.Type.SALES_ORDER,
                            fromId: salesOrderId,
                            toType: record.Type.INVOICE
                        } );

                        log.debug( "Invoice generated", newInvoiceRecord );

                        //Finding the line items count from invoice record
                        let lineCountItemSublist = newInvoiceRecord.getLineCount( { sublistId: 'item' } );
                        log.debug( "lineCountItemSublist", lineCountItemSublist );

                        //creating itemList object with item id and quantity using for loop from item fulfillment
                        for ( let i = 0; i < lineCountItemSublist; i++ )
                        {
                            try
                            {
                                if ( checkItemType( newIFRecord, i ) )
                                {
                                    itemList.push( itemListElement( newIFRecord, i ) );//creating an array of objects to store IF contents
                                }
                            } catch ( err )
                            {
                                log.debug( 'Error at lineCountItemSublist', err );
                                log.error( 'Error at lineCountItemSublist', err );
                            }
                        }
                        log.debug( "Item list object array", itemList );

                        //Adding quantity values to sublist in invoice using itemList object
                        addQtyRemoveLinesOnInvoice( newInvoiceRecord, itemList );

                        // deleteInvoiceLine( newInvoiceRecord, itemList );

                        //Setting value for item fulfillment# in invoice record. IF Reference number
                        newInvoiceRecord.setValue( {
                            fieldId: 'custbody_jj_if_no_new',
                            value: itemFulfillId,
                            ignoreFieldChange: true
                        } );

                        newInvoiceRecord.setValue( {
                            fieldId: 'approvalstatus',
                            value: approvalStatus, //1 stands for 'pending approval' and 2 for 'approved'
                            ignoreFieldChange: true
                        } );

                        //---------------Adding code for qr code encoding in template------------//

                        let sellerNameId = newInvoiceRecord.getValue( {
                            fieldId: 'subsidiary'
                        } );
                        let sellerText = newInvoiceRecord.getText( {
                            fieldId: 'subsidiary'
                        } );
                        //log.debug( "sellerText", sellerText )

                        let invoiceID = newInvoiceRecord.getValue( { fieldId: 'id' } );
                        log.debug( " invoiceID", invoiceID )

                        //search for subsidiary record from invoice
                        let resultSet = subsidiarySearch( sellerNameId );
                        //fetching the seller/customer name
                        let customerName = resultSet[ 0 ].sellerName;
                        //search to encode customer name
                        let encodedCustName = encodingString( customerName )
                        //log.debug(" encodedCustName", encodedCustName)

                        //get the length of the customer name
                        let custNameLength = getLength( encodedCustName )

                        //setting the tag value
                        tagHex = '01'

                        //concatinating the tag value hex value of length and encoded customer name
                        let sellerNameNew = tagHex.concat( custNameLength, encodedCustName )
                        //log.debug("sellerNameNew", sellerNameNew)

                        let vatNum = resultSet[ 0 ].vatNumber;
                        log.debug( "vatNum", vatNum )
                        // if (vatNum != '' || vatNum != null || vatNum != undefined) {

                        let encodedVatNum = encodingString( vatNum )
                        log.debug( "encodedVatNum", encodedVatNum )

                        let vatNumLength = getLength( encodedVatNum )
                        //log.debug("vatNumLength", vatNumLength)

                        tagHex = '02'
                        let vatNumNew = tagHex.concat( vatNumLength, encodedVatNum )
                        // } else {
                        //     let vatNumLength = 0
                        //     let encodedVatNum = ''
                        //     let vatNumNew = tagHex.concat(vatNumLength, encodedVatNum)
                        // }
                        if ( vatNum == '' || vatNum == null || vatNum == undefined )
                        {
                            let vatNumLength = 0
                            let encodedVatNum = ''
                            let vatNumNew = tagHex.concat( vatNumLength, encodedVatNum )
                        }
                        //log.debug( "vatNumNew", vatNumNew )

                        let invoiceTotal = newInvoiceRecord.getValue( { fieldId: 'total' } );

                        let invoiceTotal1 = invoiceTotal.toFixed( 2 )
                        let encodedStringInvoiceTotal = encodingString( invoiceTotal1 )
                        let invoiceTotalLength = getLength( encodedStringInvoiceTotal )
                        // log.debug(" encodedStringInvoiceTotal", encodedStringInvoiceTotal)

                        tagHex = '04'
                        let invoiceTotalNew = tagHex.concat( invoiceTotalLength, encodedStringInvoiceTotal )
                        log.debug( " invoiceTotalNew", invoiceTotalNew )

                        let vatTotal = newInvoiceRecord.getValue( { fieldId: 'taxtotal' } );

                        let vatTotal1 = vatTotal.toFixed( 2 )
                        let encodedStringVatTotal = encodingString( vatTotal1 )
                        let vatTotalLength = getLength( encodedStringVatTotal )
                        //log.debug("base64EncodedStringVatTotal",base64EncodedStringVatTotal)

                        tagHex = '05'
                        let vatTotalNew = tagHex.concat( vatTotalLength, encodedStringVatTotal )
                        log.debug( "vatTotalNew", vatTotalNew )

                        // SAVE THE TRANSFORMED INVOICE
                        let invSaveObjID = newInvoiceRecord.save( {
                            enableSourcing: true,
                            ignoreMandatoryFields: true
                        } );
                        log.debug( "Saved invoice is", invSaveObjID );

                        //Logic to get time stamp - Invoice record create date and time
                        let timeNew = transactionSearch( invSaveObjID )
                        log.debug( "timeNew1", timeNew )

                        let timeStampUTC = timeNew + " UTC"
                        log.debug( "timeStampUTC", timeStampUTC )
                        let today = new Date( timeStampUTC )
                        log.debug( "today", today )
                        let timeNew1 = today.toISOString()
                        log.debug( "ISO", timeNew1 )
                        let encodededTimeStamp = encodingString( timeNew1 )
                        let timStampLength = getLength( encodededTimeStamp )
                        // log.debug("encodededTimeStamp",encodededTimeStamp )

                        tagHex = '03'
                        let timeStampNew = tagHex.concat( timStampLength, encodededTimeStamp )
                        log.debug( "timeStampNew ", timeStampNew )

                        // concatinating the encoded seller name, vat number,time stamp,tax total and vat total
                        let encodedQrCode = sellerNameNew + vatNumNew + timeStampNew + invoiceTotalNew + vatTotalNew
                        log.debug( "encodedQrCode", encodedQrCode )

                        //encoding the result to base 64 format
                        let base64EncodedString = encode.convert( {
                            string: encodedQrCode,
                            inputEncoding: encode.Encoding.HEX,
                            outputEncoding: encode.Encoding.BASE_64
                        } );
                        log.debug( "base64EncodedString", base64EncodedString );

                        record.submitFields( {
                            type: record.Type.INVOICE,
                            id: invSaveObjID,
                            values: {
                                'custbody_jj_qr_code': base64EncodedString
                            }
                        } );
                        //----------------QR code encoding code ends in previous line------------//

                        //setting value for Invoice# field in item fulfillment
                        record.submitFields( {
                            type: record.Type.ITEM_FULFILLMENT,
                            id: itemFulfillId,
                            values: {
                                'custbody_jj_pdf_inv_amvop_121': invSaveObjID
                            }
                        } );

                    } else
                    {
                        log.debug( "Fulfillment status is not shipped, no invoice created." )
                    }
                }
            } catch ( err )
            {
                log.debug( { title: "Error at afterSubmit function body", details: err } );
                log.error( { title: "Error at afterSubmit function body", details: err } );
            }
        }

        return { afterSubmit }

        /**
         * Function to check if the current user has access to the invoice automation script
         * @param {Object} currentUser - Object referring current user
         * @returns {boolean} - returns true if user has access else returns false
         */
        function checkUser ( currentUser )
        {
            try
            {
                let userId = currentUser.id, userRole = currentUser.role;
                log.debug( { title: "Current user", details: userId } );
                log.debug( { title: "Current user role", details: userRole } );

                let invAutomationUser = runtime.getCurrentScript().getParameter( { name: "custscript_jj_inv_auto_user" } );
                log.debug( { title: "User obtained from parameters", details: invAutomationUser } );//this value is returned as an id
                log.debug( { title: "User role specified in code", details: invAutomationUserRole } );//this value is returned as an id

                if ( userId == invAutomationUser && userRole == invAutomationUserRole )
                {
                    log.debug( "User has access to automation script" );
                    return true;
                }
                else
                {
                    log.debug( "User is not authorized for invoice automation" );
                    return false;
                }
            } catch ( err )
            {
                log.debug( "Error found at checkUser() function body" );
                log.error( "Error found at checkUser() function body" );
            }
        }

        /**
         * Function to check if the item type is assembly or inventory and if yes return true
         * @param {Object} itemFulfillRecObject - Object referring to the item fulfillment record created to trigger the script
         * @param {Number} line - Number value representing the current line value in IF record referring the item
         * @returns {boolean} - true for assembly and inventory types else returns false
         */
        function checkItemType ( itemFulfillRecObject, line )
        {
            try
            {
                let itemTypeValue = itemFulfillRecObject.getSublistValue( {
                    sublistId: 'item',
                    fieldId: 'itemtype',
                    line: line
                } );
                if ( itemTypeValue === "Assembly" || itemTypeValue === "InvtPart" )
                {//checking if item is assembly or inventory item. Add other types if required
                    return true;
                }
                else
                {
                    log.debug( "Invoice automation not available for item type" );
                    return false;
                }
            } catch ( err )
            {
                log.debug( "Error at checkItemType() function", err );
                log.error( "Error at checkItemType() function", err );
            }
        }


        /**
         * Function to return an object with item id, order line value and quantity
         * @param {Object} itemFulfillRecObject - Object referring to the item fulfillment record created to trigger the script
         * @param {Number} line - Number value representing the current line value in IF record
         * @returns {{itemQuantityValue: *, itemIdValue: *, itemOrderLine: *}}
         */
        function itemListElement ( itemFulfillRecObject, line )
        {
            try
            {
                //Returns null value for quantity if not present in item fulfillment
                let itemId = itemFulfillRecObject.getSublistValue( {
                    sublistId: 'item',
                    fieldId: 'item',
                    line: line
                } );

                let itemQuantity = itemFulfillRecObject.getSublistValue( {
                    sublistId: 'item',
                    fieldId: 'quantity',
                    line: line
                } );

                let orderLineValue = itemFulfillRecObject.getSublistValue( {
                    sublistId: 'item',
                    fieldId: 'orderline',
                    line: line
                } );

                let itemObject1 = { itemIdValue: itemId, itemOrderLine: orderLineValue, itemQuantityValue: itemQuantity };
                return itemObject1;

            } catch ( err )
            {
                log.debug( "Error at itemListElement() function", err );
                log.error( "Error at itemListElement() function", err );
            }
        }

        /**
         * Function to add quantity value to each line item in invoice record if it is present in fulfillment
         * @param invoiceRecObject - object representing the new invoice generated by the script
         * @param itemList - Array with item id, orderline value, quantity represented as objects
         */
        function addQtyRemoveLinesOnInvoice ( invoiceRecObject, itemList )
        {
            try
            {
                //let addedQtyArray = []
                let invoiceLine = invoiceRecObject.getLineCount( { sublistId: 'item' } );//since invoice created from sales order it have all items in sales order

                //loop through the invoice object
                for ( let i = 0; i < invoiceLine; i++ )
                {
                    let orderLineValue = invoiceRecObject.getSublistValue( {
                        sublistId: 'item',
                        fieldId: 'orderline',
                        line: i
                    } );
                    log.debug( "orderLineValue", orderLineValue )

                    //Find matching value from the itemlist (IF) based of off the orderline
                    let obj = itemList.find( obj => obj.itemOrderLine == orderLineValue );
                    log.debug( "obj", obj )

                    //set qty if orderline matches or null
                    if ( obj )
                    {
                        invoiceRecObject.setSublistValue( {//adding quantity obtained from itemList object
                            sublistId: 'item',
                            fieldId: 'quantity',
                            line: i,
                            value: obj.itemQuantityValue
                        } );
                    }
                    else
                    {
                        invoiceRecObject.setSublistValue( {//adding quantity obtained from itemList object
                            sublistId: 'item',
                            fieldId: 'quantity',
                            line: i,
                            value: ""
                        } );
                    }

                    //addedQtyArray.push( itemListElementofInvoice( invoiceRecObject, i ) )
                }
                //log.debug( "XYYZ addedQtyArray", addedQtyArray )

                //loops to remove item lines in invoce
                for ( var i = invoiceLine - 1; i >= 0; i-- )
                {

                    if ( invoiceRecObject.getSublistValue( { sublistId: 'item', fieldId: 'quantity', line: i } ) == "" )
                    {
                        let orderLineValue = invoiceRecObject.getSublistValue( {
                            sublistId: 'item',
                            fieldId: 'orderline',
                            line: i
                        } );
                        log.debug( "removed lines", orderLineValue )

                        invoiceRecObject.removeLine( {// Removing unwanted lines from item sublist
                            sublistId: 'item',
                            line: i
                        } );
                    }
                }

            } catch ( err )
            {
                log.debug( "Error in addQtyRemoveLinesOnInvoice() function", err );
                log.error( "Error in addQtyRemoveLinesOnInvoice() function", err );
            }
        }

        /**
         * @param function to get the length of the parameters and convert the string to hex value
         * @param returns hexadecimal value of string
         */
        function getLength ( lengthparam )
        {
            try
            {
                let length = lengthparam.length
                let orgLength = Number( length ) / 2
                log.debug( "length", length )
                let hexString = orgLength.toString( 16 )
                if ( hexString.length < 2 )
                {
                    hexString = "0" + hexString;
                }
                //log.debug("hexString", hexString)
                return hexString
            } catch ( e )
            {
                log.debug( "Error@getLength", e )
                log.error( "Error@getLength", e );
            }
        }

        /**
         * @param function accepts UTF_8 format and returns hexadecimal value of input
         * @param returns encoded string
         */

        function encodingString ( params )
        {
            try
            {
                let hexEncodedString = encode.convert( {
                    string: params,
                    inputEncoding: encode.Encoding.UTF_8,
                    outputEncoding: encode.Encoding.HEX
                } );
                //log.debug("hexEncodedString",hexEncodedString)
                return ( hexEncodedString )
            } catch ( e )
            {
                log.debug( "Error@encodingString", e );
                log.error( "Error@encodingString", e );
            }
        }

        /**
         * @param function to search subsidiary to get the seller name(arabic) if absent it returns name
         * @param returns name
         */
        function subsidiarySearch ( sellerNameId )
        {
            try
            {
                let subsidiaryArray = [];
                var subsidiarySearchObj = search.create( {
                    type: "subsidiary",
                    filters:
                        [
                            [ "internalid", "anyof", sellerNameId ]
                        ],
                    columns:
                        [
                            search.createColumn( { name: "taxidnum", label: "Tax ID" } ),
                            search.createColumn( {
                                name: "formulatext",
                                formula: "NVL2({custrecord_az_mn_sub_namearabic}, {custrecord_az_mn_sub_namearabic}, {name})",
                                label: "seller name"
                            } )
                        ]
                } );

                subsidiarySearchObj.run().each( function ( result )
                {
                    let vatNumber = result.getValue( result.columns[ 0 ] );
                    let sellerName = result.getValue( result.columns[ 1 ] );
                    let customRecordObj = {};
                    customRecordObj.vatNumber = vatNumber
                    customRecordObj.sellerName = sellerName
                    subsidiaryArray.push( customRecordObj );
                    return true;
                } );
                return subsidiaryArray;

            } catch ( e )
            {
                log.debug( "Error@subsidiarySearch", e );
                log.error( "Error@subsidiarySearch", e )
            }
        }

        /**
         * 
         * @param {number} invSaveObjID - internal id of invoice
         * @returns formattedtime
         */
        function transactionSearch ( invSaveObjID )
        {
            try
            {
                var transactionSearchObj = search.create( {
                    type: "transaction",
                    filters:
                        [
                            [ "internalidnumber", "equalto", invSaveObjID ],
                            "AND",
                            [ "mainline", "is", "T" ],
                            "AND",
                            [ "systemnotes.type", "is", "T" ],
                            "AND",
                            [ "formulanumeric: case when({systemnotes.field}='Record') then 1 else 0 end", "equalto", "1" ]
                        ],
                    columns:
                        [
                            search.createColumn( {
                                name: "date",
                                join: "systemNotes",
                                label: "Date"
                            } ),
                            search.createColumn( {
                                name: "formulatext",
                                formula: "TO_CHAR({datecreated},'MM/DD/YYYY HH:MI:SS AM')",
                                label: "Formula (Text)"
                            } ),
                            search.createColumn( {
                                name: "formulatext",
                                formula: "TO_CHAR({datecreated},'DD/MM/YYYY HH:MI:SS AM')",
                                label: "Formula (Text)"
                            } )
                        ]
                } );

                transactionSearchObj.run().each( function ( result )
                {
                    formattedTime = result.getValue( transactionSearchObj.columns[ 1 ] )
                    return true;
                } );
                return formattedTime;
            }
            catch ( e )
            {
                log.debug( "error@transactionSearch", e );
                log.error( "error@transactionSearch", e );
            }
        }
    } )

Leave a comment

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