The requirement is to send a payment reminder email if the customer is not done payment after 1 hour of sales order creation from the website.
We need to consider the following conditions for sending the payment reminder email:
Condition For sending Email:
The following conditions need to be true.
- The payment confirmation checkbox(a custom field created for checking payment status) should be unchecked (False) for the corresponding sales order.
- The sales order creation date will be today.
- The source of Sale order creation will be the Web.
- Sales orders were created one hour ago.
- Payment Reminder Email checkbox is False
If all these conditions are true then an email will send to the customer.
Script:
Note: This script uses an email template (for payment reminder) for sending emails.
The script uses a search with filters as the above-mentioned conditions and uses the ‘reduce’ function for sending emails.
/**
* @NApiVersion 2.1
* @NScriptType MapReduceScript
*/
/*****************************************************************************************************************
* Author: Jobin & Jismi
* Client Name: Nickolls and Perks Limited
* Date Created : 28-Feb-2023
* Created By: JJ0147
* Script Description:The script will check the sales orders with no payment record and sends payment reminder email to
* corresponding customers.
*
*****************************************************************************************************************/
define(['N/https', 'N/record', 'N/search', 'N/email', 'N/render' , 'N/runtime'],
/**
* @param{https} https
* @param{record} record
* @param{search} search
* @param{nEmail} nEmail
* @param{render} render
* @param{runtime} runtime
*/
(https, record, search, nEmail, render, runtime) => {
/**
* 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 {
log.debug("Inside getInputData");
var salesorderSearchObj = search.create({
type: "salesorder",
filters:
[
["type","anyof","SalesOrd"],
"AND",
["source","anyof","NLWebStore"],
"AND",
["datecreated","onorbefore","hoursago1"],
"AND",
["datecreated","within","today"],
"AND",
["terms","anyof","14"],
"AND",
["custbody_payment_reminder_sent","is","F"],
"AND",
["custbody_payment_confirmed_checkbox","is","F"],
"AND",
["mainline","is","T"],
"AND",
["applyingtransaction","anyof","@NONE@"]
],
columns:
[
search.createColumn({name: "tranid", label: "Document Number"}),
search.createColumn({name: "terms", label: "Terms"}),
search.createColumn({name: "source", label: "Source"}),
search.createColumn({name: "custbody_payment_confirmed_checkbox", label: "Payment_Confirmation"}),
search.createColumn({name: "custbody_payment_reminder_sent", label: "Payment reminder email"}),
search.createColumn({name: "datecreated", label: "Date Created"}),
search.createColumn({name: "email", label: "Email"})
]
});
var searchResultCount = salesorderSearchObj.runPaged().count;
log.debug("salesorderSearchObj result count",searchResultCount);
let salesOrderListResult = [];
salesorderSearchObj.run().each(function(result){
// .run().each has a limit of 4,000 results
salesOrderListResult.push(result);
return true;
});
log.debug("salesOrderListResult", salesOrderListResult);
return salesOrderListResult;
} catch (err) {
log.debug("Error in scheduled script execute function", err);
}
}
/**
* 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("Inside reduce22");
let salesorderSearchObj = JSON.parse(reduceContext.values);//dataObj is obtained by returning values from getInputData() stage
log.debug("salesorderSearchObj", salesorderSearchObj);
let salesOrderInternalId = salesorderSearchObj.id;
let payment_check = salesorderSearchObj.values.custbody_payment_confirmed_checkbox
let email_check = salesorderSearchObj.values.custbody_payment_reminder_sent
let customer_emailid = salesorderSearchObj.values.email
let tranID = salesorderSearchObj.values.tranid
let tran_date = salesorderSearchObj.values.datecreated
log.debug("payment_check", payment_check);
log.debug("email_check", email_check);
log.debug("customer_emailid", customer_emailid);
log.debug("tranID", tranID);
log.debug("tran_date", tran_date);
log.debug("salesOrderInternalId", salesOrderInternalId);
var transactionNumber = parseInt(salesOrderInternalId);
log.debug("transactionNumber", transactionNumber);
let salesOrderRecordObj = record.load({type: record.Type.SALES_ORDER, id: salesOrderInternalId});
log.debug("salesOrderRecordObj", salesOrderRecordObj);
if((payment_check=== false)&&(email_check===false)) {
log.debug("if true");
// Load the email template record
var emailTemplate = record.load({
type: record.Type.EMAIL_TEMPLATE,
id:435 // Replace TEMPLATE_ID with the internal ID of your email template
}); // Set the recipient email address
log.debug("emailTemplate77",emailTemplate)
var mergeResult = render.mergeEmail({
templateId:435,
entity: {
type: 'employee',
id:118
},
transactionId: transactionNumber
});
log.debug("mergeResult.body", mergeResult.body)
var recipientEmail = customer_emailid; // Set the email subject
var emailSubject = 'Payment Reminder'; // Create the email merge record
nEmail.send({
author:118,
recipients: recipientEmail,
subject: emailSubject,
body: mergeResult.body,
relatedRecords: {transactionId: transactionNumber}
});
//setting value fof email send checkbox true
salesOrderRecordObj.setValue({
fieldId: 'custbody_payment_reminder_sent',
value: true
});
salesOrderRecordObj.save({
enableSourcing: true,
ignoreMandatoryFields: true
});
}
} catch (err) {
log.debug("Error in reduce", err);
}
}
return {getInputData, reduce}
});