define([“N/search”, “N/email”, “N/record”, “N/runtime”, “N/format”],
(search, email, record, runtime, format) => {
‘use strict’
/**
* @description the function to check whether a value exists in parameter
* @param parameter -passing parameter
* @param parameterName – passing parameter name
* @returns{Boolean}
*/
function checkForParameter(parameter, parameterName) {
try{
if (
parameter != “” &&
parameter != null &&
parameter != undefined &&
parameter != “null” &&
parameter != “undefined” &&
parameter != ” “ &&
parameter != false
) {
return true;
} else {
if (parameterName)
log.debug(
“Empty Value found”,
“Empty Value for parameter “ + parameterName
);
return false;
}
}catch(e){
log.error(“error@checkForParameter”)
return false;
}
}
/***********
* Function to get invoices that are due for 45 days
*/
function getInvoicesDueFourtyFiveDays() {
try {
let invoiceSearchObj = search.create({
type: “invoice”,
filters:
[
[“type”, “anyof”, “CustInvc”],
“AND”,
[“mainline”, “is”, “T”],
“AND”,
[“shipping”, “is”, “F”],
“AND”,
[“cogs”, “is”, “F”],
“AND”,
[“taxline”, “is”, “F”],
“AND”,
[“status”, “noneof”, “CustInvc:B”],
“AND”,
[“formulanumeric: ROUND({today}-{trandate})”, “equalto”, “45”],
“AND”,
[“custbody_jj_inv_due_email_sent”, “is”, “F”],
“AND”,
[“trandate”, “onorafter”, “10/1/2023”]
],
columns:
[
search.createColumn({ name: “internalid”, label: “Internal ID” }),
search.createColumn({ name: “tranid”, label: “Document Number” }),
search.createColumn({ name: “amountremaining”, label: “Amount Remaining” }),
search.createColumn({
name: “internalid”,
join: “customer”,
label: “Internal ID”
}),
search.createColumn({
name: “formulatext”,
formula: “NVL({customer.custentity_jj_accounting_email},{customer.email})”,
label: “Formula (Text)”
})
]
});
let resultArray = []
log.debug(“COUNT”, invoiceSearchObj.runPaged().count)
invoiceSearchObj.run().each(function (result) {
let resultObject = {}
resultObject.dueDays = 45
resultObject.internalId = result.getValue({ name: “internalid”, label: “Internal ID” })
resultObject.doumentNumber = result.getValue({ name: “tranid”, label: “Document Number” })
resultObject.amountRemaining = result.getValue({ name: “amountremaining”, label: “Amount Remaining” })
resultObject.customerIds = result.getValue({ name: “internalid”, join: “customer”, label: “Internal ID” })
resultObject.custEmails= result.getValue({
name: “formulatext”,
formula: “NVL({customer.custentity_jj_accounting_email},{customer.email})”,
label: “Formula (Text)”
})
resultArray.push(resultObject)
return true;
});
log.debug(“results 45”, resultArray)
return resultArray
} catch (e) {
log.error(“error@getInvoicesDueFourtyFiveDays”, e)
return []
}
}
/************
* Function to get invoices that are due for 30 days
************/
function getInvoicesThirtyIntervals() {
try {
let invoiceSearchObj = search.create({
type: “invoice”,
filters:
[
[“type”, “anyof”, “CustInvc”],
“AND”,
[“mainline”, “is”, “T”],
“AND”,
[“shipping”, “is”, “F”],
“AND”,
[“cogs”, “is”, “F”],
“AND”,
[“taxline”, “is”, “F”],
“AND”,
[“status”, “noneof”, “CustInvc:B”],
“AND”,
[“formulanumeric: ROUND({today}-{custbody_jj_date_intervals})”, “equalto”, “11”],
“AND”,
[“trandate”, “after”, “10/1/2023”],
“AND”,
[“custbody_jj_inv_due_email_sent”, “is”, “T”]
],
columns:
[
search.createColumn({ name: “internalid”, label: “Internal ID” }),
search.createColumn({ name: “tranid”, label: “Document Number” }),
search.createColumn({ name: “amountremaining”, label: “Amount Remaining” }),
search.createColumn({
name: “internalid”,
join: “customer”,
label: “Internal ID”
}),
search.createColumn({
name: “formulatext”,
formula: “NVL({customer.custentity_jj_accounting_email},{customer.email})”,
label: “Formula (Text)”
})
]
});
let resultArray = []
log.debug(“COUNT”, invoiceSearchObj.runPaged().count)
invoiceSearchObj.run().each(function (result) {
let resultObject = {}
resultObject.dueDays = 30
resultObject.internalId = result.getValue({ name: “internalid”, label: “Internal ID” })
resultObject.doumentNumber = result.getValue({ name: “tranid”, label: “Document Number” })
resultObject.amountRemaining = result.getValue({ name: “amountremaining”, label: “Amount Remaining” })
resultObject.customerIds = result.getValue({ name: “internalid”, join: “customer”, label: “Internal ID” })
resultObject.custEmails= result.getValue({
name: “formulatext”,
formula: “NVL({customer.custentity_jj_accounting_email},{customer.email})”,
label: “Formula (Text)”
})
resultArray.push(resultObject)
return true;
});
log.debug(“results 30”, resultArray)
return resultArray
} catch (e) {
log.error(“error@getInvoicesThirtyIntervals”, e.message)
return []
}
}
/***********
* Function to sent invoices due emails
* @param penaltyAmount : penalty required to apply for the invoice
* @param respEmail : recipients of the email
* @param documentNumber: document number of invoice
* @param customerIds : customer’s internal id
* @param penaltyApplied: percentage of the penalty
*/
function sentEmails(penaltyAmount, respEmail, doumentNumber, customerIds, penaltyApplied) {
try {
let emailBody = “Hi,” + “<br/><br/>” + “Thank you for being a valued partner and a prompt payer, we appreciate it very much. This is just a friendly reminder that the invoice “ + doumentNumber + ” is now overdue, incurring a “ + penaltyApplied + “% penalty, bringing the total amount due to $” + penaltyAmount.toFixed(2) + “<br/>” + “To avoid any potential late fees or inconvenience, kindly arrange the payment for this invoice at your earliest convenience.” + “<br/><br/>” + “Thanks.”
email.send({
author: 2528,
recipients: respEmail,
body: emailBody,
subject: “Invoice Past Due Reminder”,
relatedRecords: {
entityId: parseInt(customerIds)
}
});
return true;
} catch (e) {
log.error(“error@sentEmails”, e);
return false;
}
}
/***********
* Function to add penalty item to the line level
* @param invoiceObject : invoice’s data object
* @param penaltyPercentage : percentage of the penalty
* @param penaltyApplied : penalty applied for the invoice
*/
function addPenaltyItems(invoiceObject, penaltyPercentage, penaltyApplied, custEmailId) {
try {
let penaltyAmount = penaltyPercentage * (invoiceObject.amountRemaining)
let totalDue = Number(penaltyAmount) + Number(invoiceObject.amountRemaining)
let invObject = record.load({
type: record.Type.INVOICE,
id: invoiceObject.internalId,
isDynamic: true,
})
// Add a new item line to invoice
invObject.selectNewLine({
sublistId: ‘item’,
});
invObject.setCurrentSublistValue({
sublistId: ‘item’,
fieldId: ‘item’,
value: 5087
});
invObject.setCurrentSublistValue({
sublistId: ‘item’,
fieldId: ‘1’,
value: 1
});
invObject.setCurrentSublistValue({
sublistId: ‘item’,
fieldId: ‘amount’,
value: penaltyAmount
});
invObject.commitLine({
sublistId: ‘item’
})
if (checkForParameter(custEmailId)) {
log.debug(“Entered ***”)
let emailSent = sentEmails(totalDue, custEmailId, invoiceObject.doumentNumber, invoiceObject.customerIds, penaltyApplied)
let ordDate = new Date();
let newDate = ordDate.getDate(), newMonth = ordDate.getMonth() + 1, newYear = ordDate.getFullYear();
let newFormattedDate = newMonth + “/” + newDate + “/” + newYear;
log.debug(“newFormattedDate”, newFormattedDate)
let parsedDateIs = format.parse({
value: newFormattedDate,
type: format.Type.DATE,
});
if (emailSent && invoiceObject.dueDays == 45) {
invObject.setValue({
fieldId:‘custbody_jj_date_intervals’,
value: parsedDateIs
});
invObject.setValue({
fieldId:‘custbody_jj_inv_due_email_sent’,
value: true
});
invObject.save({
enableSourcing: true,
ignoreMandatoryFields: true
});
}
else if (emailSent && invoiceObject.dueDays == 30) {
invObject.setValue({
fieldId:‘custbody_jj_date_intervals’,
value: parsedDateIs
});
invObject.save({
enableSourcing: true,
ignoreMandatoryFields: true
});
}
}
} catch (e) {
log.error(“error@addPenaltyItems”, e)
return false;
}
}
/**
* 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 invoiceOrderArrayFourtyFive = getInvoicesDueFourtyFiveDays()
let invoiceArrayThirty = getInvoicesThirtyIntervals()
if (invoiceOrderArrayFourtyFive.length > 0 || invoiceArrayThirty.length > 0) {
log.debug(“RESULTS”, invoiceOrderArrayFourtyFive.concat(invoiceArrayThirty))
return invoiceOrderArrayFourtyFive.concat(invoiceArrayThirty);
} else {
return [];
}
} 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 {
let invoiceObject = JSON.parse(reduceContext.values)
let scriptObj = runtime.getCurrentScript();
let penaltyPercentageFourtyFive = scriptObj.getParameter({ name: ‘custscript_jj_penalty_percentage45’ });
let penaltyPercentageThirty = scriptObj.getParameter({ name: ‘custscript_jj_penalty_percentage30’ });
let custEmailId=invoiceObject.custEmails
if (invoiceObject.dueDays == 45) {
let penaltyfourtyFive = (Number(penaltyPercentageFourtyFive)) / 100
log.debug(“penaltyfourtyFive 45”, penaltyfourtyFive)
addPenaltyItems(invoiceObject, penaltyfourtyFive, penaltyPercentageFourtyFive,custEmailId)
} else if (invoiceObject.dueDays == 30) {
let penaltyThirty = (Number(penaltyPercentageThirty)) / 100
log.debug(“penaltyThirty 30”, penaltyThirty)
addPenaltyItems(invoiceObject, penaltyThirty, penaltyPercentageThirty,custEmailId)
}
} catch (e) {
log.error(“error@reduce”, e)
}
}
return { getInputData, reduce }
});