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 }
});