Payment Notification to Customer Contact

Map/Reduce Script to send the payment notification email for Billing Contact of Customer, using Merge template function.

Script uses all externalized, searches, email templates etc.

/**
 * @NApiVersion 2.1
 * @NScriptType MapReduceScript
 * @NModuleScope SameAccount
 */
/*************************************************************************************
 Update tax item for customers
* Description : Map/Reduce Script to send the payment notification email for Billing Contact of Customer.
*************************************************************************************/
define(['N/format', 'N/record', 'N/search', 'N/email', 'N/runtime', 'N/render', './grw_020_common_library.js'],
    /**
 * @param{format} format
 * @param{record} record
 * @param{search} search
 * @param{commonLibrary} commonLibrary
 */
    (format, record, search, email, runtime, render, commonLibrary) => {
        // Global variable for storing errors ----> for error handling & debugging process.
        const SCRIPT_PARAM_EMAIL_AUTHOR_ID = 'custscript_grw_020_email_author'
        const SCRIPT_PARAM_CR_SEARCH_ID = 'custscript_grw_020_custom_rec_searchid'
        /**
         * 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
         */


        const getInputData = (inputContext) => {
            try {
                let paymentNotificationArray = exports.getPaymentNotificationDetails();
                log.debug("paymentNotificationArray", paymentNotificationArray);


                //Fetch the custom record Payment Nitification Entries
                let paymentNoticationFinalArray = [];


                for (let i = 0; i < paymentNotificationArray.length; i++) {


                    let paymentStatusSearchId = paymentNotificationArray[i].paymentQueue.value;
                    let emailTemplate = paymentNotificationArray[i].emailTemplate.value;


                    let contactSearchArray = exports.getBillingContactDetails(paymentNotificationArray[i].contactSearch.value);
                    //log.debug("contactSearchArray", contactSearchArray);


                    let contactGroupedbyCustomerData = contactSearchArray.reduce((result, entry) => {
                        let customerId = entry.CustomerId.value;
                        result[customerId] = result[customerId] || [];
                        result[customerId].push(entry.InternalID.value);
                        return result;
                    }, {});
                    log.debug("contactGroupedbyCustomerData", contactGroupedbyCustomerData)


                    let paymentStatusArr = exports.getPaymentStatusEmailNotification(paymentStatusSearchId, emailTemplate, contactGroupedbyCustomerData);
                    log.debug("paymentStatusArr", paymentStatusArr);


                    paymentNoticationFinalArray.push(paymentStatusArr);


                }
                log.debug("paymentNoticationFinalArray-FINAL", paymentNoticationFinalArray);
                paymentNoticationFinalArray = paymentNoticationFinalArray.flat();


                return paymentNoticationFinalArray;


            } catch (e) {
                log.error('error@getInputData', e);
                return []
            }
        }
        /**
         * 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("reduceContext", reduceContext);
                let paymentNotificationRecArr = JSON.parse(reduceContext.values);
                log.debug("paymentNotificationRecArr", paymentNotificationRecArr);


                let pyId = paymentNotificationRecArr.PaymentID.value
                let tranId = Number(pyId)
                log.debug("tranID", tranId);


                let contactIdArray = paymentNotificationRecArr.contacts;
                log.debug("contact Ids", contactIdArray);


                let authorId = runtime.getCurrentScript().getParameter({ name: SCRIPT_PARAM_EMAIL_AUTHOR_ID });


                // Send email notification to Billing Contact of Customer with payment details.
                if (contactIdArray.length > 0 && tranId) {


                    let emailTemplateId = paymentNotificationRecArr.emailTemplate;
                    for (let contactVal = 0; contactVal < contactIdArray.length; contactVal++) {


                        let recipientEmail = contactIdArray[contactVal]
                        log.debug("recipientEmail", recipientEmail)
                        if (recipientEmail) {


                            // Load the email template
                            let emailTemplate = render.mergeEmail({
                                templateId: emailTemplateId,
                                entity: null, // Set to null for non-transactional templates
                                // recipient: {
                                //     type: 'contact',
                                //     id: recipientEmail
                                // },
                                supportCaseId: null, // Set to null if not using support case
                                transactionId: isNaN(tranId) ? null : tranId, // Set to null for non-transactional templates
                                customRecord: null
                            });
                            // Send the email
                            email.send({
                                author: authorId,
                                recipients: recipientEmail,
                                subject: emailTemplate.subject,
                                body: emailTemplate.body
                            });
                            let updatePaymentRecord = exports.updatePaymentNotificationSent(tranId);
                        }
                    }
                    log.debug("email send succesfully");
                }


            }
            catch (e) {
                log.error('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) => {
            // Summarize added to find the Map/Reduce script excution end time.
        }






        const exports = {
            /**
             * @description Fetch the custom record Payment Notification details.
             * @param {*} customRecType 
             * @returns 
             */
            getPaymentNotificationDetails() {
                try {
                    let customRecDetailsArray = [];
                    let customRecSearchId = runtime.getCurrentScript().getParameter({ name: SCRIPT_PARAM_CR_SEARCH_ID });
                    let customRecordgrw020PaymentNotificationObj = search.load({
                        id: customRecSearchId
                    });
                
                    customRecDetailsArray = commonLibrary.iterateSavedSearch(customRecordgrw020PaymentNotificationObj, commonLibrary.fetchSavedSearchColumn(customRecordgrw020PaymentNotificationObj, 'label'));
                    return customRecDetailsArray;


                }
                catch (e) {
                    log.error('error@getPaymentNotificationDetails', e);
                }
            },
            /**
             * @description Retrieve the search result of Payment email notification for the status either approve or failed.
             * @param {*} paymentStatusSearchId 
             * @param {*} emailTemplate 
             * @returns 
             */
            getPaymentStatusEmailNotification(paymentStatusSearchId, emailTemplate, contactGroupedbyCustomerData) {
                try {
                    let paymentStatusEmailNotificationObj = search.load({
                        id: paymentStatusSearchId
                    });
                    let paymentArr = commonLibrary.iterateSavedSearch(paymentStatusEmailNotificationObj, commonLibrary.fetchSavedSearchColumn(paymentStatusEmailNotificationObj, 'label'));


                    let additionalObject = { "emailTemplate": emailTemplate };


                    let resultArray = paymentArr.map(entry => {
                        let customerId1 = entry.Name.value;
                        let contacts = contactGroupedbyCustomerData[customerId1] || [];


                        return { ...entry, ...additionalObject, contacts };
                    });


                    log.debug("resultArray", resultArray);
                    return resultArray;
                }
                catch (e) {
                    log.error('error@getPaymentStatusDepositEmailNotification', e);
                }
            },
            /**
             * @description Retrieve the contact search result.
             * @param {*} contactSearchId 
             * @returns 
             */
            getBillingContactDetails(contactSearchId) {
                try {
                    let contactSearchObj = search.load({
                        id: contactSearchId
                    });
                    return commonLibrary.iterateSavedSearch(contactSearchObj, commonLibrary.fetchSavedSearchColumn(contactSearchObj, 'label'));
                }
                catch (e) {
                    log.error('error@getBillingContactDetails', e);
                }
            },
            /**
             * @description Check the Payment Notification Sent field of Payment record.
             * @param {*} paymentDetails 
             */
            updatePaymentNotificationSent(tranId) {
                try {
                    let id = record.submitFields({
                        type: record.Type.CUSTOMER_PAYMENT,
                        id: tranId,
                        values: {
                            custbody_grw019_paymentnotificatsent: true
                        },
                        options: {
                            enableSourcing: false,
                            ignoreMandatoryFields: true
                        }
                    });
                }
                catch (Err) {
                    log.error('error@updatePaymentNotificationSent', Err);
                }
            }
        }


        return { getInputData, reduce, summarize }
    });

Leave a comment

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