The client is required to resend the delivery scheduling link to the customer as a reminder email. It needs to be sent if the customer is not scheduled the Order, need to be sent the email after every 48 hours.
/**
* @NApiVersion 2.1
* @NScriptType MapReduceScript
*/
define(['N/email', 'N/record', 'N/search','N/url','N/encode',],
/**
* @param{email} email
* @param{record} record
* @param{search} search
*/
(email, record, search, url, encode) => {
/**
* 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{
var salesorderSearchObj = search.create({
type: "salesorder",
filters:
[
["type","anyof","SalesOrd"],
"AND",
["mainline","is","F"],
"AND",
["custcol_dt_order","isempty",""],
"AND",
["custcol_trans_route","noneof","@NONE@"],
"AND",
[["custcol_jj_scheduled_email_date","isempty",""],"OR",["formulanumeric: CASE WHEN TO_NUMBER({today} - {custcol_jj_scheduled_email_date}) > 3 THEN 1 ELSE 0 END","equalto","1"]]
],
columns:
[
search.createColumn({
name: "internalid",
summary: "GROUP",
label: "Internal ID"
}),
search.createColumn({
name: "custcol_trans_route",
summary: "GROUP",
label: "Route"
}),
search.createColumn({
name: "tranid",
summary: "GROUP",
label: "Document Number"
}),
search.createColumn({
name: "altname",
join: "customerMain",
summary: "GROUP",
label: "Name"
})
]
});
return salesorderSearchObj;
}
catch(e){
log.error("error@getInputData",e);
return [];
}
}
/**
* Defines the function that is executed when the map entry point is triggered. This entry point is triggered automatically
* when the associated getInputData stage is complete. This function is applied to each key-value pair in the provided
* context.
* @param {Object} mapContext - Data collection containing the key-value pairs to process in the map stage. This parameter
* is provided automatically based on the results of the getInputData stage.
* @param {Iterator} mapContext.errors - Serialized errors that were thrown during previous attempts to execute the map
* function on the current key-value pair
* @param {number} mapContext.executionNo - Number of times the map function has been executed on the current key-value
* pair
* @param {boolean} mapContext.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} mapContext.key - Key to be processed during the map stage
* @param {string} mapContext.value - Value to be processed during the map stage
* @since 2015.2
*/
// const map = (mapContext) => {
// }
function generateURL(soId,routeId){
try {
const xorCipher = (text, key) => {
let result = '';
for (let i = 0; i < text.length; i++) {
result += String.fromCharCode(text.charCodeAt(i) ^ key.charCodeAt(i % key.length));
}
return result;
};
const timeStamp = JSON.stringify(Date.now());
const cipheredText = `${xorCipher(soId + '-' + routeId, timeStamp)}`;
log.debug("cipheredText", JSON.stringify(cipheredText));
let base64Val = encode.convert({
string: cipheredText,
inputEncoding: encode.Encoding.UTF_8,
outputEncoding: encode.Encoding.BASE_64
});
const passCode = encodeURIComponent(base64Val);
let schedulingUrl = url.resolveScript({
scriptId: 'customscript_jj_sl_fetchupd_so_ahap1890',
deploymentId: 'customdeploy_jj_sl_fetchupd_so_ahap1890',
returnExternalUrl: true,
params: {
soId: soId,
routeId: routeId,
dateStampId: timeStamp,
passCodeId: passCode
}
});
log.debug('schedulingUrl', schedulingUrl);
return schedulingUrl;
}
catch (err) {
log.error("error@sendEmailToCustomer", err);
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{
let searchResult = JSON.parse(reduceContext.values);
log.debug("searchResult",searchResult);
let deliveryURL= generateURL(searchResult.values['GROUP(internalid)']['value'], searchResult.values['GROUP(custcol_trans_route)']['value']);
//log.debug("main URL",deliveryURL);
if (deliveryURL) {
let curRec = record.load({
type: 'salesorder',
id: searchResult.values['GROUP(internalid)']['value']
});
log.debug("curRec",curRec);
let customerEmail = curRec.getValue({fieldId: 'custbody17'});
log.debug("emial",customerEmail);
let documentNumber = searchResult.values["GROUP(tranid)"];
let customerName= searchResult.values["GROUP(altname.customerMain)"];
let emailBody = `Hi ${customerName},<br><br>Please click on link below to schedule delivery for your order #${documentNumber}<br><br><a href="${deliveryURL}">Click here to schedule the order</a><br><br>Regards,<br><br>Airport Home Appliance Support Team`;
email.send({
author: 56746,
recipients: customerEmail,
subject: `Please Select The Delivery Date for Your Order #${searchResult.values['GROUP(tranid)']}`,
body: emailBody,
relatedRecords: {
transactionId: Number(searchResult.values['GROUP(internalid)']['value'])
}
});
let lineCount = curRec.getLineCount({sublistId: 'item'});
for(let i=0;i<lineCount;i++){
let route = curRec.getSublistValue({sublistId: 'item',fieldId: 'custcol_trans_route', line: i})
let dtOrder = curRec.getSublistValue({sublistId: 'item',fieldId: 'custcol_dt_order', line: i})
if((route == searchResult.values['GROUP(custcol_trans_route)']['value']) && (!dtOrder || dtOrder=='')){
curRec.setSublistValue({sublistId: 'item', fieldId: 'custcol_jj_scheduled_email_date', value: new Date(), line: i});
}
}
curRec.save();
}
}
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) => {
}
return {getInputData, reduce, summarize}
});