Restlet for creating Invoice

The following script creates invoice in Netuite based on the request values provided from Salesforce.

Also these invoice will have a pay-by-link URL autogenerated( the script trigger the Adyen Bundle to generate the link)

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

define(['N/record', 'N/search', 'N/runtime', './moment.js', 'N/format', 'N/email', 'N/error', 'N/url'],

    function (record, search, runtime, moment, format, email, error, url) {
        const DOCUMENT_DATE = "custbody_document_date"
        const DOCUMENT_PDF_LINK = "custbody_ota_documentpdflink"
        const SALESFORCE_RECORD_ID = "custbody_ota_salesforcerecordid"
        const SALESFORCE_UPDATED_DATE = "custbody_ota_salesforceupdateddate"
        const SALESFORCEURL = "custbody_ota_salesforceurl"
        const SALESFORCE_SYNCHED_DATE = "custbody_ota_sfsyncheddate"
        const SALESFORCE_TRANSACTION_EMAIL_SENT_DATETIME = "custbody_ota_transactionemailsentdate"
        const OTA_SERVICE_PERIOD_START_DATE = "custcol_ota_serviceperiodstartdate"
        const OTA_SERVICE_PERIOD_END_DATE = "custcol_ota_serviceperiodenddate"
        const OTA_INV_BATCH_CUSTOMER_NO = "custbody_ota_invoicebatchcustomer"
        const STATUS_REC_TYPE = "customrecord_grw_003_rec_intgrn_status"
        const STATUS_FLD_TRAN_TYPE = "custrecord_grw_003_tran_type"
        const STATUS_FLD_ERROR_MSSG = "custrecord_grw_003_json_response"
        const STATUS_FLD_RESQUEST_JSON = "custrecord_grw_003_req_json"
        const STATUS_STAT_CODE = "custrecord_grw_003_statuscode"
        const ADYEN_AMT_OVERRIDE = "custbody_es_adyen_auth_amount_override"
        const ADYEN_SUPPRESS_BILL_ADDRESS = "custbody_es_adyen_suppress_billaddress"
        const ADYEN_PAY_BY_LINK_CONFIG = "custbody_es_adyen_pbl_config"
        const ENTITY_INV_BATCH = "custentity_ota_invoicebatch"
        const CUST_ADYEN_SUPPRESS_BILL_ADDRESS = "custentity_ota_invoicebatch.custrecord_grw_005_3_suppres_billaddress"
        const CUST_ADYEN_AMT_OVERRIDE = "custentity_ota_invoicebatch.custrecord_grw_005_3_amt_override"
        const CUST_ADYEN_PBL_CONFIG = "custentity_ota_invoicebatch.custrecord_grw_005_3_adn_pbl_config"

        /**
         * Post action data handling and triggers Invoice creation
         * @param {*} context - json object request from api call
         * @returns 
         */
        function doPost(context) {
            let responseObj = {};
            let requestBody = context
            log.debug('requestBody', requestBody);

            if (!isEmpty(requestBody)) {
                let invObjJson = requestBody

                let user = runtime.getCurrentUser();
                let datePref = user.getPreference({
                    name: 'DATEFORMAT'
                });

                if (!isEmpty(invObjJson)) {
                    // Get Currency
                    let invCurrency = invObjJson.currency

                    // Get Document Date
                    let invDocDate = invObjJson.custbody_document_date
                    if (!isEmpty(invDocDate)) {
                        invDocDate = moment(invDocDate).format(datePref)
                    }

                    // Get Transaction Date
                    let invTranDate = invObjJson.tranDate
                    if (!isEmpty(invTranDate)) {
                        invTranDate = moment(invTranDate).format(datePref)
                    }

                    // Get Due Date
                    let invDueDate = invObjJson.dueDate
                    if (!isEmpty(invDueDate)) {
                        invDueDate = moment(invDueDate).format(datePref)
                    }

                    let invDocPdfLink = invObjJson.custbody_ota_documentpdflink
                    let invSfRecId = invObjJson.custbody_ota_salesforcerecordid

                    // Get value for Salesforce Updated Date
                    let invSfUpdateDate = invObjJson.custbody_ota_salesforceupdateddate
                    if (!isEmpty(invSfUpdateDate)) {
                        invSfUpdateDate = format.format({ value: new Date(invSfUpdateDate), type: format.Type.DATETIME })
                    }

                    let invSfUrl = invObjJson.custbody_ota_salesforceurl

                    // Get value for Salesforce Synched Date field
                    let invSfSyncDate = invObjJson.custbody_ota_sfsyncheddate
                    if (!isEmpty(invSfSyncDate)) {
                        invSfSyncDate = format.format({ value: new Date(invSfSyncDate), type: format.Type.DATETIME })
                    }

                    // Get Data for Salesforce Transaction Email Sent DateTime field
                    let invEmailSentDate = invObjJson.custbody_ota_transactionemailsentdate
                    if (!isEmpty(invEmailSentDate)) {
                        invEmailSentDate = format.format({ value: new Date(invEmailSentDate), type: format.Type.DATETIME })
                    }

                    //Set customer
                    let invEntity = invObjJson.entity

                    //Set External ID (unique identifier for records)
                    let invExternalId = invSfRecId

                    //Get item object
                    let invItem = invObjJson.item

                    //Get Memo
                    let invMemo = invObjJson.memo

                    let invSubsidiary = invObjJson.subsidiary

                    //Get Document number
                    let invTranId = invObjJson.tranId

                    // Get Location value (Mandatory Field in inv record, so if it null system will throw error)
                    let invLocation = invObjJson.location

                    let invCostCenter = invObjJson.department

                    if (!isEmpty(invExternalId)) {
                        // For duplicate detection using External Id
                        let extId = invAlreadyExits(invExternalId)

                        if (!extId) {
                            let invId = createNSInvoice(invCurrency, invDocDate, invDocPdfLink, invSfRecId, invSfUpdateDate, invSfUrl, invSfSyncDate, invEmailSentDate, invDueDate, invEntity, invExternalId, invItem, invMemo, invSubsidiary, invTranDate, datePref, invTranId, invLocation, invCostCenter)
                            log.debug("invId", invId)

                            if (Number.isInteger(invId)) {
                                responseObj.statusCode = "SUCCESS";
                                responseObj.recordId = invId;
                                return JSON.stringify(responseObj);
                            }
                            else {
                                responseObj.statusCode = "FAILURE";
                                responseObj.error = invId;
                                let statusRecId = createStatusRecord(responseObj, requestBody)
                            }
                        }
                        else {
                            responseObj.statusCode = "FAILURE";
                            responseObj.error = "This record already exists";
                            let statusRecId = createStatusRecord(responseObj, requestBody)
                        }
                    }
                    else {
                        responseObj.statusCode = "FAILURE";
                        responseObj.error = "External Id is missing";
                        let statusRecId = createStatusRecord(responseObj, requestBody)
                    }
                }
            }
        }

        /**
         * Creation of Invoice
         */
        function createNSInvoice(invCurrency, invDocDate, invDocPdfLink, invSfRecId, invSfUpdateDate, invSfUrl, invSfSyncDate, invEmailSentDate, invDueDate, invEntity, invExternalId, invItem, invMemo, invSubsidiary, invTranDate, datePref, invTranId, invLocation, invCostCenter) {
            let invResponse;
            try {
                let invRecord = record.create({
                    type: record.Type.INVOICE,
                    isDynamic: true
                });

                invRecord.setValue({
                    fieldId: 'entity',
                    value: invEntity
                });

                if (!isEmpty(invSubsidiary)) {
                    invRecord.setValue({
                        fieldId: 'subsidiary',
                        value: invSubsidiary
                    });
                }

                if (!isEmpty(invCurrency)) {
                    invRecord.setValue({
                        fieldId: 'currency',
                        value: invCurrency
                    });
                }

                invRecord.setValue({
                    fieldId: 'externalid',
                    value: invExternalId
                })

                if (!isEmpty(invTranDate)) {
                    invRecord.setText({
                        fieldId: 'trandate',
                        text: invTranDate
                    })
                }
                else {
                    invRecord.setValue({
                        fieldId: 'trandate',
                        value: new Date()
                    })
                }

                if (!isEmpty(invTranId)) {
                    invRecord.setValue({
                        fieldId: 'tranid',
                        value: invTranId
                    });
                }

                invRecord.setValue({
                    fieldId: 'location',
                    value: invLocation
                });

                if (!isEmpty(invCostCenter)) {
                    invRecord.setValue({
                        fieldId: 'department',
                        value: invCostCenter
                    });
                }

                try {
                    if (!isEmpty(invDocDate)) {
                        invRecord.setText({
                            fieldId: DOCUMENT_DATE,
                            text: invDocDate
                        });
                    }

                    if (!isEmpty(invDocPdfLink)) {
                        invRecord.setValue({
                            fieldId: DOCUMENT_PDF_LINK,
                            value: invDocPdfLink
                        });
                    }

                    if (!isEmpty(invSfRecId)) {
                        invRecord.setValue({
                            fieldId: SALESFORCE_RECORD_ID,
                            value: invSfRecId
                        });
                    }

                    if (!isEmpty(invSfUpdateDate)) {
                        invRecord.setText({
                            fieldId: SALESFORCE_UPDATED_DATE,
                            text: invSfUpdateDate
                        });
                    }

                    if (!isEmpty(invSfUrl)) {
                        invRecord.setValue({
                            fieldId: SALESFORCEURL,
                            value: invSfUrl
                        });
                    }

                    if (!isEmpty(invSfSyncDate)) {
                        invRecord.setText({
                            fieldId: SALESFORCE_SYNCHED_DATE,
                            text: invSfSyncDate
                        });
                    }

                    if (!isEmpty(invEmailSentDate)) {
                        invRecord.setText({
                            fieldId: SALESFORCE_TRANSACTION_EMAIL_SENT_DATETIME,
                            text: invEmailSentDate
                        });
                    }

                    if (!isEmpty(invDueDate)) {
                        invRecord.setText({
                            fieldId: 'duedate',
                            text: invDueDate
                        })
                    }

                    if (!isEmpty(invMemo)) {
                        invRecord.setValue({
                            fieldId: 'memo',
                            value: invMemo
                        })
                    }
                } catch (e) {
                    log.error("Err@skipcustomFields", e)
                }

                if (!isEmpty(invItem)) {
                    let invItems = invItem.items

                    if (!isEmpty(invItems)) {
                        let itemLineCount = invItems.length

                        for (let index = 0; index < itemLineCount; index++) {
                            let lineItem = invItems[index].item

                            if (!isEmpty(lineItem)) {
                                invRecord.selectNewLine({
                                    sublistId: 'item'
                                });
                                invRecord.setCurrentSublistValue({
                                    sublistId: 'item',
                                    fieldId: 'item',
                                    value: lineItem,
                                    ignoreFieldChange: false,
                                    forceSyncSourcing: true
                                });

                                invRecord.setCurrentSublistValue({
                                    sublistId: 'item',
                                    fieldId: 'quantity',
                                    value: invItems[index].quantity
                                });

                                invRecord.setCurrentSublistValue({
                                    sublistId: 'item',
                                    fieldId: 'rate',
                                    value: invItems[index].rate
                                });

                                invRecord.setCurrentSublistValue({
                                    sublistId: 'item',
                                    fieldId: 'tax1amt',
                                    value: invItems[index].taxamount
                                });

                                try {
                                    let servicePeriodStartdate = invItems[index].custcol_ota_serviceperiodstartdate
                                    if (!isEmpty(servicePeriodStartdate)) {
                                        servicePeriodStartdate = moment(servicePeriodStartdate).format(datePref)
                                        invRecord.setCurrentSublistText({
                                            sublistId: 'item',
                                            fieldId: OTA_SERVICE_PERIOD_START_DATE,
                                            text: servicePeriodStartdate
                                        });
                                    }

                                    let servicePeriodEnddate = invItems[index].custcol_ota_serviceperiodenddate
                                    if (!isEmpty(servicePeriodEnddate)) {
                                        servicePeriodEnddate = moment(servicePeriodEnddate).format(datePref)
                                        invRecord.setCurrentSublistText({
                                            sublistId: 'item',
                                            fieldId: OTA_SERVICE_PERIOD_END_DATE,
                                            text: servicePeriodEnddate
                                        });
                                    }

                                    if (!isEmpty(invItems[index].department)) {
                                        invRecord.setCurrentSublistValue({
                                            sublistId: 'item',
                                            fieldId: 'department',
                                            value: invItems[index].department
                                        });
                                    }
                                } catch (Err) {
                                    log.error("Errskipcustomlinefields", Err)
                                }
                                invRecord.commitLine({
                                    sublistId: 'item'
                                });
                            }
                        }
                    }
                }

                let InvMapfieldLookUp = search.lookupFields({
                    type: record.Type.CUSTOMER,
                    id: invEntity,
                    columns: [CUST_ADYEN_SUPPRESS_BILL_ADDRESS, CUST_ADYEN_AMT_OVERRIDE, ENTITY_INV_BATCH, CUST_ADYEN_PBL_CONFIG]
                });
                log.debug("InvMapfieldLookUp", InvMapfieldLookUp)

                if (!isEmpty(InvMapfieldLookUp)) {
                    let amtOverride = InvMapfieldLookUp[CUST_ADYEN_AMT_OVERRIDE]
                    if (!isEmpty(amtOverride)) {
                        invRecord.setValue({
                            fieldId: ADYEN_AMT_OVERRIDE,
                            value: amtOverride
                        });
                    }

                    let supBillAddress = InvMapfieldLookUp[CUST_ADYEN_SUPPRESS_BILL_ADDRESS]
                    if (!isEmpty(supBillAddress)) {
                        invRecord.setValue({
                            fieldId: ADYEN_SUPPRESS_BILL_ADDRESS,
                            value: supBillAddress
                        });
                    }

                    let hasEntityInvBatch = InvMapfieldLookUp[ENTITY_INV_BATCH]
                    if (!isEmpty(hasEntityInvBatch)) {
                        let entityInvBatch = hasEntityInvBatch[0].text;
                        if (!isEmpty(entityInvBatch)) {
                            invRecord.setValue({
                                fieldId: OTA_INV_BATCH_CUSTOMER_NO,
                                value: entityInvBatch
                            });
                        }
                    }

                    let hasAdyenPblConfig = InvMapfieldLookUp[CUST_ADYEN_PBL_CONFIG]
                    if (!isEmpty(hasAdyenPblConfig)) {
                        let adyenPblConfig = hasAdyenPblConfig[0].value;
                        if (!isEmpty(adyenPblConfig)) {
                            invRecord.setValue({
                                fieldId: ADYEN_PAY_BY_LINK_CONFIG,
                                value: adyenPblConfig
                            });
                        }
                    }
                }

                let recordID = invRecord.save({
                    enableSourcing: true,
                    ignoreMandatoryFields: true
                });
                log.debug("recordID", recordID);
                invResponse = recordID
            }
            catch (Err) {
                invResponse = Err.message;

                log.error("Err@Sales Order Creation", Err)
            }
            return invResponse;
        }


        /**
         * Duplicate validation of transaction using external id
         * @param {*} invExtId 
         * @returns 
         */
        function invAlreadyExits(invExtId) {
            let resultval;
            try {
                let invoiceSearchObj = search.create({
                    type: "invoice",
                    filters:
                        [
                            ["type", "anyof", "CustInvc"],
                            "AND",
                            ["externalid", "anyof", invExtId]
                        ],
                    columns:
                        [
                            search.createColumn({
                                name: "internalid",
                                summary: "GROUP",
                                label: "Internal ID"
                            })

                        ]
                });
                let searchResultCount = invoiceSearchObj.runPaged().count;
                log.debug("invoiceSearchObj result count", searchResultCount);
                invoiceSearchObj.run().each(function (result) {
                    resultval = result.getValue({
                        name: "internalid",
                        summary: "GROUP",
                        label: "Internal ID"
                    });
                    log.debug("resultval", resultval)

                    return false;
                });
            } catch (e) {
                log.error("Err@invAlreadyExits", e)
            }
            return resultval ? resultval : false;
        }

        /**
         * Custom record creation for error handling
         * @param {*} reponseJson 
         * @param {*} requestBody 
         * @returns 
         */
        function createStatusRecord(reponseJson, requestBody) {
            let statusRecObj = record.create({
                type: STATUS_REC_TYPE,
                isDynamic: true
            });
            statusRecObj.setValue({
                fieldId: STATUS_FLD_TRAN_TYPE,
                value: "Invoice Generation API"
            });
            statusRecObj.setValue({
                fieldId: STATUS_FLD_ERROR_MSSG,
                value: reponseJson.error
            });
            statusRecObj.setValue({
                fieldId: STATUS_STAT_CODE,
                value: reponseJson.statusCode
            });
            statusRecObj.setValue({
                fieldId: STATUS_FLD_RESQUEST_JSON,
                value: requestBody
            });

            let statusRecId = statusRecObj.save({
                enableSourcing: true,
                ignoreMandatoryFields: true
            });

            sendEmailNotification(statusRecId)

            var missingIdError = error.create({
                name: reponseJson.statusCode,
                message: reponseJson.error,
                notifyOff: true
            });

            missingIdError.toString = function () { return missingIdError.message };

            throw missingIdError;
        }


        /**
         * Send Email if errors blocks transaction creation
         * @param {*} statusRecId 
         */
        function sendEmailNotification(statusRecId) {
            try {
                let senderId = runtime.getCurrentUser().id;
                let recipientId = 242 //113; // For now we set the receipient Test Employee, will be updated as -5 later;

                let output = url.resolveRecord({
                    recordType: STATUS_REC_TYPE,
                    recordId: statusRecId,
                    isEditMode: true
                });

                if (output) {
                    let extractedInfo = extractRecordInfoFromUrl(output);

                    log.debug('Record Type:', extractedInfo.recordType);
                    log.debug('Record ID:', extractedInfo.recordId);
                    log.debug('account Id:', extractedInfo.accntId);

                    let nsBaseUrl = 'https://' + extractedInfo.accntId + '.app.netsuite.com/app/common/custom/custrecordentry.nl?rectype=';
                    let recLink = '<a href="' + nsBaseUrl + extractedInfo.recordType + '&id=' + extractedInfo.recordId + '"> View Record </a>';
                    log.debug("recLink1", recLink)

                    let emailSubject = "Alert: Invoice Creation API Failure"

                    let emailBody = "<BR />Hi, <BR/> <BR/>This email is to notify you that the Invoice Generation triggered from SalesForce has failed exceution. <BR/> Please find the failure reason from the below record" + " " + recLink + "."

                    email.send({
                        author: senderId,
                        recipients: recipientId,
                        subject: emailSubject,
                        body: emailBody,
                        relatedRecords: {
                            entityId: recipientId
                        }
                    });
                }
            } catch (e) {
                log.error("Err@sendEmailNotification", e)
            }
        }


        /**
         * validating variable values has value or not
         * @param {*} value 
         * @returns 
         */
        function isEmpty(value) {
            try {
                let isEmptyObject = function (a) {
                    if (typeof a.length === 'undefined') { // it's an Object, not an Array
                        let hasNonempty = Object.keys(a).some(function nonEmpty(element) {
                            return !isEmpty(a[element]);
                        });
                        return hasNonempty ? false : isEmptyObject(Object.keys(a));
                    }
                    return !a.some(function nonEmpty(element) { // check if array is really not empty as JS thinks
                        return !isEmpty(element); // at least one element should be non-empty
                    });
                };
                return (
                    value == false
                    || typeof value === 'undefined'
                    || value == null
                    || (typeof value === 'object' && isEmptyObject(value))
                );
            } catch (Err) {
                log.error("Err@extractRecordInfoFromUrl", Err)
            }
        }

        /**
         * To fetch Url Components of the Custom Record
         * @param {*} url 
         * @returns 
         */
        function extractRecordInfoFromUrl(url) {
            try {
                // Define regular expressions to match the parameters
                let rectypeRegex = /rectype=(\d+)/;
                let idRegex = /id=(\d+)/;
                let idAccntRegex = /compid=(\d+)/;

                let matchRectype = url.match(rectypeRegex);
                let matchId = url.match(idRegex);
                let matchAccId = url.match(idAccntRegex);

                return {
                    recordType: matchRectype[1],
                    recordId: matchId[1],
                    accntId: matchAccId[1]
                };
            } catch (Err) {
                log.error("Err@extractRecordInfoFromUrl", Err)
            }
        }

        return {
            'post': doPost
        }
    });

Leave a comment

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