Automated Emails For Invoices And Credit Memo

The invoice emails including the credit memo and invoices will be sent on a scheduled basis. The requirement can be achieved by creating a custom record for scheduling purposes. The process will work as follows:

  1. A custom record will be created containing the following fields –
  1. Author
  2. Email Template
  3. Email Subject
  4. Email Body
/**
 * @NApiVersion 2.1
 * @NScriptType MapReduceScript
 */
define(['N/currentRecord', 'N/email', 'N/record', 'N/redirect', 'N/render', 'N/runtime', 'N/search', 'N/ui/serverWidget', 'N/url'],
    /**
     * @param{currentRecord} currentRecord
     * @param{email} email
     * @param{record} record
     * @param{redirect} redirect
     * @param{render} render
     * @param{runtime} runtime
     * @param{search} search
     * @param{serverWidget} serverWidget
     * * @param{url} url
     */
    (currentRecord, email, record, redirect, render, runtime, search, serverWidget, url) => {
        /**
         * Defines the function that is executed at the beginning of the map/reduce process and generates the input data.
         * @param {Object} inputContext
         * @param {boolean} inputContext.isRestarted - Indicates whether the current invocation of this function is the first
         *     invocation (if true, the current invocation is not the first invocation and this function has been restarted)
         * @param {Object} inputContext.ObjectRef - Object that references the input data
         * @typedef {Object} ObjectRef
         * @property {string|number} ObjectRef.id - Internal ID of the record instance that contains the input data
         * @property {string} ObjectRef.type - Type of the record instance that contains the input data
         * @returns {Array|Object|Search|ObjectRef|File|Query} The input data to use in the map/reduce process
         * @since 2015.2
         */


        //Search created to retrieve contacts of a customer with email type is receive invoices and credits.
        //custentity_emial_type is a custom multi select field created in the contact record

        function contactSearch(custId) {
            var customerSearchObj = search.create({
                type: "customer",
                filters:
                    [
                        ["internalid", "anyof", custId],
                        "AND",
                        ["contact.custentity_emial_type", "anyof", "1"]
                    ],
                columns:
                    [
                        search.createColumn({name: "internalid", label: "Internal ID"}),
                        search.createColumn({
                            name: "email",
                            join: "contact",
                            label: "Email"
                        })
                    ]
            });
            var searchResultCount = customerSearchObj.runPaged().count;
            var contactSearchResult = customerSearchObj.run().getRange({
                start: 0,
                end: 1000
            });


            log.debug('SearchCount in contact search', searchResultCount)
            return contactSearchResult;
        }

        const getInputData = (inputContext) => {

            try {
                log.debug('In Getinput')
                //Search created to get invoices and credit memo with open status that are created today
                var transactionSearchObj = search.create({
                    type: "transaction",
                    filters:
                        [
                            // ["datecreated", "on", "today"],
                            // "AND",

                            ["internalid", "anyof", "232", "2567"],
                            "AND",
                            ["type", "anyof", "CustInvc", "CustCred"],
                            "AND",
                            ["status", "anyof", "CustCred:A", "CustInvc:A"],
                            "AND",
                            ["mainline", "is", "T"],
                            "AND",
                            ["customer.custentity_emial_type", "anyof", "1"]
                        ],
                    columns:
                        [
                            search.createColumn({name: "internalid", label: "Internal ID"}),
                            search.createColumn({name: "tranid", label: "Document Number"}),
                            search.createColumn({name: "trandate", label: "Date"}),
                            search.createColumn({name: "datecreated", label: "Date Created"}),
                            search.createColumn({name: "asofdate", label: "As-Of Date"}),
                            search.createColumn({name: "type", label: "Type"}),
                            search.createColumn({name: "entity", label: "Name"}),
                            search.createColumn({name: "memo", label: "Memo"}),
                            search.createColumn({name: "amount", label: "Amount"}),
                            search.createColumn({
                                name: "internalid",
                                join: "customer",
                                label: "Internal ID"
                            }),
                            search.createColumn({
                                name: "email",
                                join: "customer",
                                label: "Email"
                            }),
                            search.createColumn({
                                name: "altname",
                                join: "customer",
                                label: "Name"
                            })
                        ]
                });
                var searchResultCount = transactionSearchObj.runPaged().count;
                var searchResult = transactionSearchObj.run().getRange({
                    start: 0,
                    end: 1000
                });


                log.debug('SearchCount', searchResultCount)
                log.debug('SearchResuLT Data', searchResult)

                return searchResult;

            } catch (e) {
                log.debug('Error@GetInputData', e)
            }

        }


        /**
         * Defines the function that is executed when the reduce entry point is triggered. This entry point is triggered
         * automatically when the associated map stage is complete. This function is applied to each group in the provided context.
         * @param {Object} reduceContext - Data collection containing the groups to process in the reduce stage. This parameter is
         *     provided automatically based on the results of the map stage.
         * @param {Iterator} reduceContext.errors - Serialized errors that were thrown during previous attempts to execute the
         *     reduce function on the current group
         * @param {number} reduceContext.executionNo - Number of times the reduce function has been executed on the current group
         * @param {boolean} reduceContext.isRestarted - Indicates whether the current invocation of this function is the first
         *     invocation (if true, the current invocation is not the first invocation and this function has been restarted)
         * @param {string} reduceContext.key - Key to be processed during the reduce stage
         * @param {List<String>} reduceContext.values - All values associated with a unique key that was passed to the reduce stage
         *     for processing
         * @since 2015.2
         */






        const reduce = (reduceContext) => {


            try {
                log.debug('In Reduce')
                let dataObj = JSON.parse(reduceContext.values)
                log.debug('Dataobj', dataObj)

                //Load custom record created for getting the author , email subject and email body.
                let bulkEmailerCustRecord = record.load({
                    type: 'customrecord_invoice_rcrd_schdle_hgpl84',
                    id: 1,
                    isDynamic: true

                })
                log.debug('Bulk emailer record', bulkEmailerCustRecord);
                //Get author field value from custom record.
                var author = bulkEmailerCustRecord.getValue({
                    fieldId: 'custrecord_author'
                });
                log.debug('AUTHOR', author)

                //Get internal Id of invoice or credit  memo
                var intId = dataObj.values.internalid[0].value
                log.debug('InternalId', intId)

                //Get customer email
                var rec = dataObj.values["customer.email"]

                //Get author from custom record
                var sub = bulkEmailerCustRecord.getValue({
                    fieldId: 'custrecord_author'
                });

                //Get email template from the custom record
                var temp = bulkEmailerCustRecord.getValue({
                    fieldId: 'custrecord_email_template'
                });
                log.debug('Templete value',temp)
                log.debug('Receipt', rec)

                //Get customer id from the search result
                var custId = dataObj.values["customer.internalid"][0].value
                log.debug('Cust id', custId)

                //Calling the contact search to get the contact emails of the customer id passing to it.
                var ccContact = contactSearch(custId)

                log.debug('Contact search res', ccContact)
                var contacts = [];

                //Storing the cc contacts to an array
                for (var i = 0; i < ccContact.length; i++) {
                    contacts.push(ccContact[i].getValue({
                        name: "email",
                        join: "contact",
                        label: "Email"
                    }))
                }
                log.debug('Contact array', contacts)

                //Create pdf of invoice
                var invoice = render.transaction({
                    entityId: parseInt(dataObj.id),
                    printMode: render.PrintMode.PDF
                })
                log.debug('Invoice', invoice)


                //function to call the suitlet script with the given id and deployment id
                function callSuiltelet(intId) {
                    var suiteletURL = url.resolveScript({
                        scriptId: 767,
                        deploymentId: 'customdeploy_jj_sl_invcrdtmemo_view_hgpl',
                        returnExternalUrl: false,
                        params: {
                            'custscriptcust_id': custId,
                        }
                    });
                    return suiteletURL;

                }

                //Invoking the function to call the suitelet script
                var sUrl = callSuiltelet(intId);
                log.debug('called suitelet', sUrl)

                //Creating a invoice view link for to add in the email body
                var emailUrl = "<a href='" + sUrl + "'>Open Invoice Details</a>"
                log.debug('Email Url', emailUrl)

                //If there is a template in the custom record created, executes the following code
                if(temp){
                    var emailTemplLoad = render.mergeEmail({
                        templateId: bulkEmailerCustRecord.getValue({
                            fieldId: 'custrecord_email_template'
                        })
                    });
                    log.debug('EmailTEMPLTE', emailTemplLoad)
                    var emailSubject = emailTemplLoad.subject;
                    var emailBody = emailTemplLoad.body;



                    // email.send({
                    //     author: bulkEmailerCustRecord.getValue({
                    //         fieldId: 'custrecord_author'
                    //     }),
                    //     recipients: dataObj.values["customer.email"],
                    //     cc: contacts,
                    //     subject: emailSubject,
                    //     body: emailBody + '<br><br>' + 'Please find below link to open your Invoice Details' + '<br><br>' + emailUrl,
                    //     attachments: [invoice]
                    // });


                }
                //If there is no email template added in the custom record created, excutes the following code with the given email subject and the body
                else{

                    //Code to send email with author from custom record, receipt as customer email, email body from custom record, and with PDF attachment
                    // email.send({
                    //     author: bulkEmailerCustRecord.getValue({
                    //         fieldId: 'custrecord_author'
                    //     }),
                    //     recipients: dataObj.values["customer.email"],
                    //     cc: contacts,
                    //     subject: bulkEmailerCustRecord.getValue({
                    //         fieldId: 'custrecord_email_subject'
                    //     }),
                    //     body: (bulkEmailerCustRecord.getValue({
                    //         fieldId: 'custrecord_email_body'
                    //     })) + '<br><br>' + 'Please find below link to open your Invoice Details' + '<br><br>' + emailUrl,
                    //     attachments: [invoice],
                    //
                    // });
                }

            } catch (e) {
                log.debug('Error@Reduce', e)
            }


        }


        /**
         * Defines the function that is executed when the summarize entry point is triggered. This entry point is triggered
         * automatically when the associated reduce stage is complete. This function is applied to the entire result set.
         * @param {Object} summaryContext - Statistics about the execution of a map/reduce script
         * @param {number} summaryContext.concurrency - Maximum concurrency number when executing parallel tasks for the map/reduce
         *     script
         * @param {Date} summaryContext.dateCreated - The date and time when the map/reduce script began running
         * @param {boolean} summaryContext.isRestarted - Indicates whether the current invocation of this function is the first
         *     invocation (if true, the current invocation is not the first invocation and this function has been restarted)
         * @param {Iterator} summaryContext.output - Serialized keys and values that were saved as output during the reduce stage
         * @param {number} summaryContext.seconds - Total seconds elapsed when running the map/reduce script
         * @param {number} summaryContext.usage - Total number of governance usage units consumed when running the map/reduce
         *     script
         * @param {number} summaryContext.yields - Total number of yields when running the map/reduce script
         * @param {Object} summaryContext.inputSummary - Statistics about the input stage
         * @param {Object} summaryContext.mapSummary - Statistics about the map stage
         * @param {Object} summaryContext.reduceSummary - Statistics about the reduce stage
         * @since 2015.2
         */
        const summarize = (summaryContext) => {

        }

        return {getInputData, reduce, summarize}

    });
/**
 * @NApiVersion 2.1
 * @NScriptType Suitelet
 */
define(['N/currentRecord', 'N/record', 'N/redirect', 'N/render', 'N/runtime', 'N/search', 'N/task', 'N/url', 'N/ui/serverWidget', 'N/file'],
    /**
     * @param{currentRecord} currentRecord
     * @param{record} record
     * @param{redirect} redirect
     * @param{render} render
     * @param{runtime} runtime
     * @param{search} search
     * @param{task} task
     * @param{url} url
     * @param{serverWidget} serverWidget,
     * @param{file} file
     */
    (currentRecord, record, redirect, render, runtime, search, task, url, serverWidget, file) => {
        /**
         * Defines the Suitelet script trigger point.
         * @param {Object} scriptContext
         * @param {ServerRequest} scriptContext.request - Incoming request
         * @param {ServerResponse} scriptContext.response - Suitelet response
         * @since 2015.2
         */

//Customer search created with criteria considering top level parent also to get details of transactions
        function invoiceCreditMemoSearch(parms) {
            var transactionSearchObj = search.create({
                type: "customer",
                filters:
                    [
                        [["toplevelparent.internalid", "anyof", parms], "OR", ["internalid", "anyof", parms]],
                        "AND",
                        ["transaction.type", "anyof", "CustInvc", "CustCred"],
                        "AND",
                        ["transaction.status", "anyof", "CustCred:A", "CustInvc:A"],
                        "AND",
                        ["transaction.mainline", "is", "T"]
                    ],
                columns:
                    [
                        search.createColumn({name: "internalid", label: "Internal ID"}),
                        search.createColumn({name: "altname", label: "Name"}),
                        search.createColumn({
                            name: "tranid",
                            join: "transaction",
                            label: "Document Number"
                        }),
                        search.createColumn({
                            name: "trandate",
                            join: "transaction",
                            label: "Date"
                        }),
                        search.createColumn({
                            name: "subsidiary",
                            join: "transaction",
                            label: "Subsidiary"
                        }),
                        search.createColumn({
                            name: "duedate",
                            join: "transaction",
                            label: "Due Date/Receive By"
                        }),
                        search.createColumn({
                            name: "daysoverdue",
                            join: "transaction",
                            label: "Days Overdue"
                        }),
                        search.createColumn({
                            name: "amount",
                            join: "transaction",
                            label: "Amount"
                        }),
                        search.createColumn({
                            name: "amountremaining",
                            join: "transaction",
                            label: "Amount Remaining"
                        }),
                        search.createColumn({
                            name: "internalid",
                            join: "transaction",
                            label: "Internal ID"
                        }),
                        search.createColumn({
                            name: "type",
                            join: "transaction",
                            label: "Type"
                        })
                    ]
            });
            var searchResultCount = transactionSearchObj.runPaged().count;
            var searchResult = transactionSearchObj.run().getRange({
                start: 0,
                end: 1000
            });


            log.debug('SearchCount', searchResultCount)
            log.debug('SearchResuLT Data', searchResult)

            return searchResult;

        }

        const onRequest = (scriptContext) => {

            try {

                //Get Method
                if (scriptContext.request.method === 'GET') {
                    var form = serverWidget.createForm({title: 'INVOICE DETAILS'});

                    // var scriptObj = runtime.getCurrentScript();
                    // log.debug('SCRIPT: ', scriptObj);
                    // var invoiceId = scriptObj.getParameter({name: 'custscriptinvoice_id'});
                    // log.debug('Invoice Id s',invoiceId)


                    var parms = scriptContext.request.parameters.custscriptcust_id;


                    log.debug('Invoice id from url', parms)

                    //Sublist is added to the form
                    var sublistDet = form.addSublist({
                        id: 'custpage_det',
                        type: serverWidget.SublistType.LIST,
                        label: 'Invoice Details'
                    });


                    //Adding the body fields to the suitelet

                    var date = sublistDet.addField({
                        id: 'custpage_date',
                        label: 'DATE',
                        type: serverWidget.FieldType.TEXT,
                        align: serverWidget.LayoutJustification.RIGHT
                    });
                    var docnum = sublistDet.addField({
                        id: 'custpage_docnumber',
                        label: 'DOCUMENT NUMBER',
                        type: serverWidget.FieldType.TEXT,
                        align: serverWidget.LayoutJustification.RIGHT
                    });
                    var sub = sublistDet.addField({
                        id: 'custpage_subsidiary',
                        label: 'SUBSIDIARY',
                        type: serverWidget.FieldType.TEXT,
                        align: serverWidget.LayoutJustification.RIGHT
                    });
                    var name = sublistDet.addField({
                        id: 'custpage_name',
                        label: 'CUSTOMER NAME',
                        type: serverWidget.FieldType.TEXT,
                        align: serverWidget.LayoutJustification.RIGHT
                    });


                    var duedate = sublistDet.addField({
                        id: 'custpage_duedate',
                        label: 'DUE DATE',
                        type: serverWidget.FieldType.TEXT,
                        align: serverWidget.LayoutJustification.RIGHT
                    });

                    var amount = sublistDet.addField({
                        id: 'custpage_amount',
                        label: 'AMOUNT',
                        type: serverWidget.FieldType.TEXT,
                        align: serverWidget.LayoutJustification.RIGHT
                    });
                    var amtrem = sublistDet.addField({
                        id: 'custpage_amountf',
                        label: 'AMOUNT REMAINING',
                        type: serverWidget.FieldType.TEXT,
                        align: serverWidget.LayoutJustification.RIGHT
                    });
                    var dueday = sublistDet.addField({
                        id: 'custpage_duedays',
                        label: 'DUE DAYS',
                        type: serverWidget.FieldType.TEXT,
                        align: serverWidget.LayoutJustification.RIGHT
                    });

                    //Adding the download option for each invoices
                    var downlink = sublistDet.addField({
                        id: 'custpage_down',
                        type: serverWidget.FieldType.URL,
                        label: ' '
                    }).linkText = 'Download'

                    //Invoking the customer search created inside a function and passing the customer id as a parameter to it.
                    var searchResult = invoiceCreditMemoSearch(parms);
                    log.debug('Searchresuslt count', searchResult.length)
                    log.debug('SearchResult', searchResult)

                    //Clling the client script
                    form.clientScriptFileId = 5913;

                    //Iterating through the search result.
                    for (var i = 0; i < searchResult.length; i++) {

                        //If the type of transaction is Invoice then following URl is taken
                        if ((searchResult[i].getValue({
                            name: "type",
                            join: "transaction",
                            label: "Type"
                        })) === 'CustInvc') {
                            var urlVal = "https://6687177-sb1.app.netsuite.com/app/accounting/print/hotprint.nl?regular=T&sethotprinter=T&formnumber=148&trantype=custinvc&&id=" + searchResult[i].getValue({
                                name: "internalid",
                                join: "transaction",
                                label: "Internal ID"
                            }) + "&label=Invoice&printtype=transaction"
                        }
                        //If the transaction type is a credit memo, then the following URL is taken
                        else {
                            var urlVal = "https://6687177-sb1.app.netsuite.com/app/accounting/print/hotprint.nl?regular=T&sethotprinter=T&formnumber=139&trantype=custcred&&id=" + searchResult[i].getValue({
                                name: "internalid",
                                join: "transaction",
                                label: "Internal ID"
                            }) + "&label=Credit+Memo&printtype=transaction&whence="

                        }

                        //Setting the URL value to the download link
                        sublistDet.setSublistValue({
                            id: 'custpage_down',
                            line: i,
                            value: urlVal
                        });

                        //Setting the Name
                        sublistDet.setSublistValue({
                            id: 'custpage_name',
                            line: i,
                            value: (searchResult[i].getValue({name: "altname", label: "Name"}
                            )) || " "
                        });

                        //Setting the date
                        sublistDet.setSublistValue({
                            id: 'custpage_date',
                            line: i,
                            value: (searchResult[i].getValue({
                                    name: "trandate",
                                    join: "transaction",
                                    label: "Date"
                                }
                            )) || " "
                        });

                        //Setting the document number
                        sublistDet.setSublistValue({
                            id: 'custpage_docnumber',
                            line: i,
                            value: (searchResult[i].getValue({
                                    name: "tranid",
                                    join: "transaction",
                                    label: "Document Number"
                                }
                            )) || " "
                        });

                        //Setting the Amount
                        sublistDet.setSublistValue({
                            id: 'custpage_amount',
                            line: i,
                            value: (searchResult[i].getValue({
                                    name: "amount",
                                    join: "transaction",
                                    label: "Amount"
                                }
                            )) || " "
                        });

                        //Setting the due date
                        sublistDet.setSublistValue({
                            id: 'custpage_duedate',
                            line: i,
                            value: (searchResult[i].getValue({
                                    name: "duedate",
                                    join: "transaction",
                                    label: "Due Date/Receive By"
                                }
                            )) || " "
                        });

                        //Setting the amount remaining value
                        sublistDet.setSublistValue({
                            id: 'custpage_amountf',
                            line: i,
                            value: (searchResult[i].getValue({
                                    name: "amountremaining",
                                    join: "transaction",
                                    label: "Amount Remaining"
                                }
                            )) || " "
                        });

                        //Setting the subsidiary
                        sublistDet.setSublistValue({
                            id: 'custpage_subsidiary',
                            line: i,
                            value: (searchResult[i].getText({
                                    name: "subsidiary",
                                    join: "transaction",
                                    label: "Subsidiary"
                                }
                            )) || " "
                        });

                        //Setting the due days
                        sublistDet.setSublistValue({
                            id: 'custpage_duedays',
                            line: i,
                            value: (searchResult[i].getValue({
                                    name: "daysoverdue",
                                    join: "transaction",
                                    label: "Days Overdue"
                                }
                            )) || " "
                        });

                    }

                  //Adding a button to download the CSV file, the function here invoked is defined in the client script
                    form.addButton({
                        id: 'downcsv',
                        label: 'Download CSV',
                        functionName: 'csvSave'
                    });


                    scriptContext.response.writePage(form);

                }

            } catch (e) {
                log.debug('Error@On Request', e)
            }

        }


        return {onRequest}

    });

Leave a comment

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