The credits applied cannot be fetched in the beforeSubmit and afterSubmit contexts from the payment record. It can be accessed in the beforeLoad context.
* @NApiVersion 2.1
* @NScriptType UserEventScript
*/
define(['N/record', 'N/search', 'N/ui/serverWidget', 'N/email', 'N/render', 'N/format'],
/**
* @param{record} record
* @param{search} search
* @param{serverWidget} serverWidget
*/
(record, search, serverWidget, email, render, format) => {
const CURRENCY_MAP = { //prod
"CAD": '$',
"USD": '$',
"NZD": '$',
"EUR": '€',
"GBP": '£',
"AUD": '$'
}
/**
* Function to get the date in required format
* @param {string} dateValue
* @returns {string}
*/
function formatDate(dateValue) {
if (dateValue) {
let formattedDate = format.parse({ value: dateValue, type: format.Type.DATE });
return format.format({ value: formattedDate, type: format.Type.DATE, format: 'DD/MM/YYYY' });
}
return '';
}
/**
* Function to get the total value for the transactions from which credit amount is applied.
* @param {string} refNum
* @returns {object}
*/
function getTotalValue(refNum) {
try {
let transactionSearchObj = search.create({
type: "transaction",
filters:
[
["type", "anyof", "CustCred", "CustPymt", "Journal"],
"AND",
["numbertext", "is", refNum],
"AND",
["mainline", "is", "T"]
],
columns:
[
search.createColumn({ name: "amount", label: "Amount" }),
search.createColumn({ name: "amountremaining", label: "Amount Remaining" })
]
});
let totalamt, remainingAmt;
transactionSearchObj.run().each(function (result) {
totalamt = parseFloat(result.getValue({ name: "amount", label: "Amount" }));
remainingAmt = parseFloat(result.getValue({ name: "amountremaining", label: "Amount Remaining" }));
return false;
});
return {
totalamt: totalamt,
remainingAmt: remainingAmt
};
} catch (e) {
log.error("error@getTotalValue", e);
return {};
}
}
/**
* Function to set the applied to transaction details in the email template
* @param {object} newRec
* @returns {object}
*/
function setAppliesToValueToEmailTemPlate(newRec, currencySymbol) {
try {
let lineCount = newRec.getLineCount({ sublistId: 'apply' });
let tableRows = '';
let totalPayment = 0;
for (let i = 0; i < lineCount; i++) {
let applySelected = newRec.getSublistValue({
sublistId: 'apply',
fieldId: 'apply',
line: i
});
if (applySelected == true) {
let applyDate = newRec.getSublistValue({
sublistId: 'apply',
fieldId: 'applydate',
line: i
});
// Format the date in the desired format (DD/MM/YYYY)
let formattedDate;
if (applyDate) {
formattedDate = formatDate(applyDate)
}
let trasactionType = newRec.getSublistValue({
sublistId: 'apply',
fieldId: 'trantype',
line: i
});
let invoiceNum = newRec.getSublistValue({
sublistId: 'apply',
fieldId: 'refnum',
line: i
});
let type = '';
if (trasactionType == 'CustInvc') {
type = "Invoice";
}
let description = type + ' #' + invoiceNum;
let originalAmt = newRec.getSublistValue({
sublistId: 'apply',
fieldId: 'total',
line: i
});
let payment = newRec.getSublistValue({
sublistId: 'apply',
fieldId: 'amount',
line: i
});
totalPayment += parseFloat(payment);
let discount = newRec.getSublistValue({
sublistId: 'apply',
fieldId: 'disc',
line: i
});
let amountTobePaid = newRec.getSublistValue({
sublistId: 'apply',
fieldId: 'due',
line: i
});
// calculate the open balce value
let openBalance = amountTobePaid - payment - discount;
// add the table rows for the applied to details
tableRows += `<tr>` +
'<td align="center" colspan="4">' + formattedDate + '</td>' +
'<td align="center" colspan="20">' + description + '</td>' +
'<td align="right" colspan="4">' + (originalAmt ? currencySymbol + originalAmt : '') + '</td>' +
'<td align="right" colspan="4">' + (payment ? currencySymbol + payment : '') + '</td>' +
'<td align="right" colspan="4">' + (discount ? currencySymbol + discount : '') + '</td>' +
'<td align="right" colspan="4">' + currencySymbol + (openBalance.toFixed(2) || '0.00') + '</td>' +
'</tr>';
}
}
return { tableRows: tableRows, totalPayment: totalPayment.toFixed(2) };
} catch (e) {
log.error("error@setAppliesToValueToEmailTemPlate", e);
return {};
}
}
/**
* Function to set the credir applied details in the email template
* @param {object} newRec
* @param {string} currencySymbol
* @returns {object}
*/
function setCreditValues(newRec, currencySymbol) {
try {
let lineCount = newRec.getLineCount({ sublistId: 'credit' });
let tableRows = '';
let totalCredit = 0;
if (lineCount) {
for (let i = 0; i < lineCount; i++) {
let creditSelected = newRec.getSublistValue({
sublistId: 'credit',
fieldId: 'apply',
line: i
});
if (creditSelected == true) {
let creditDate = newRec.getSublistValue({
sublistId: 'credit',
fieldId: 'creditdate',
line: i
});
// Convert applyDate to a JavaScript Date object
let formattedDate;
if (creditDate) {
formattedDate = formatDate(creditDate)
}
let trasactionType = newRec.getSublistText({
sublistId: 'credit',
fieldId: 'trantype',
line: i
});
let refNum = newRec.getSublistValue({
sublistId: 'credit',
fieldId: 'refnum',
line: i
});
let type;
if (trasactionType == 'CustInvc') {
type = "Invoice";
} else if (trasactionType == 'CustCred') {
type = "CreditMemo";
} else if (trasactionType == 'CustPymt') {
type = "Payment";
} else if (trasactionType == 'Journal') {
type = 'Journal';
} else {
type = '';
}
let description = type + ' #' + refNum;
let dueAmt = newRec.getSublistValue({
sublistId: 'credit',
fieldId: 'due',
line: i
});
let payment = newRec.getSublistValue({
sublistId: 'credit',
fieldId: 'amount',
line: i
});
totalCredit += parseFloat(payment);
let discount = newRec.getSublistValue({
sublistId: 'credit',
fieldId: 'disc',
line: i
});
let totalCreditValue = getTotalValue(refNum); // to get the balance from the corresponding record
let totalVal = Math.abs(totalCreditValue.totalamt);
let openBalance = totalCreditValue.remainingAmt || '0.00';
//Add table rows for the credits applied details
tableRows += `<tr>` +
'<td align="center" colspan="4">' + formattedDate + '</td>' +
'<td align="center" colspan="20">' + description + '</td>' +
'<td align="right" colspan="4">' + '-' + currencySymbol + totalVal + '</td>' +
'<td align="right" colspan="4">' + '-' + currencySymbol + payment + '</td>' +
'<td align="right" colspan="4">' + (discount ? currencySymbol + discount : '') + '</td>' +
'<td align="right" colspan="4">' + '-' + currencySymbol + openBalance + '</td>' +
'</tr>';
}
}
}
return { tableRows: tableRows, totalCredit: totalCredit.toFixed(2) };
} catch (e) {
log.error("error@setCreditValues", e);
return {};
}
}
/**
* Function to set the sublist values to email template
* @param {string} emailBody
* @param {object} newRec
* @returns {string}
*/
function setValuesToEmailTemplate(emailBody, newRec) {
try {
let currencyCode = newRec.getValue({
fieldId: 'currencysymbol'
});
let currencySymbol = CURRENCY_MAP[currencyCode] || '$';
let appliesToData = setAppliesToValueToEmailTemPlate(newRec, currencySymbol);
let appliesToRows = appliesToData.tableRows;
let totalPayment = appliesToData.totalPayment;
let creditsAppliedData = setCreditValues(newRec, currencySymbol);
let creditRows = creditsAppliedData.tableRows;
// Combine all rows
let allRows = appliesToRows + creditRows;
// Apply alternate row shading
let rows = allRows.split('<tr');
let tableRows = '';
// add shading to table rows
for (let i = 1; i < rows.length; i++) { // Start from 1 because split will create an empty first element
let rowColor = (i % 2 === 0) ? '#ffffff' : '#e3e3e3';
tableRows += '<tr style="background-color: ' + rowColor + ';"' + rows[i];
}
// Create the table HTML
let tableHtml = '<table class="itemtable" style="width:833px; margin-top: 10px;">' +
'<thead>' +
'<tr>' +
'<th align="center" colspan="4">Date</th>' +
'<th align="center" colspan="20">Description</th>' +
'<th align="center" colspan="4">Original Amount</th>' +
'<th align="center" colspan="4">Payment</th>' +
'<th align="center" colspan="4">Discount</th>' +
'<th align="center" colspan="4">Open Balance</th>' +
'</tr>' +
'</thead>' +
'<tbody>' + tableRows + '</tbody></table>';
let totalCredit = creditsAppliedData.totalCredit;
let total = Math.abs(totalPayment - totalCredit);
tableHtml += '</tbody></table>';
emailBody = emailBody.replace('[PAYMENT_SUMMARY_TABLE]', tableHtml);
emailBody = emailBody.replace('TOTAL_VALUE', currencySymbol+total.toFixed(2));
return emailBody;
} catch (e) {
log.error("error@setValuesToEmailTemplate", e);
return '';
}
}
/**
* Function to get the customer email
* @param {string} id
* @returns {string}
*/
function getCustomerEmail(id){
try{
let customerpaymentSearchObj = search.create({
type: "customerpayment",
filters:
[
["type","anyof","CustPymt"],
"AND",
["internalid","anyof",id],
"AND",
["mainline","is","T"]
],
columns:
[
search.createColumn({
name: "email",
join: "customer",
label: "Email"
})
]
});
let custEmail;
customerpaymentSearchObj.run().each(function(result){
custEmail = result.getValue({
name: "email",
join: "customer",
label: "Email"
})
return false;
});
return custEmail;
}catch(e){
log.error("error@getCustomerEmail",e);
return '';
}
}
/**
* Defines the function definition that is executed before record is loaded.
* @param {Object} scriptContext
* @param {Record} scriptContext.newRecord - New record
* @param {string} scriptContext.type - Trigger type; use values from the context.UserEventType enum
* @since 2015.2
*/
const beforeLoad = (scriptContext) => {
try {
let newRec = scriptContext.newRecord;
let id = newRec.id;
let recType = newRec.type;
if (recType == 'customerpayment') {
let emailSendFlag = newRec.getValue({
fieldId: 'custbody_jj_payment_receipt_flag'
});
let customerName = newRec.getText({
fieldId: 'customer'
});
let recepient = getCustomerEmail(id);
log.debug("recepient",recepient);
if (id && emailSendFlag === true) { // checking for the condition that the custom checkbox is true.
let mergeResult = render.mergeEmail({ // taking the template to add the cotent table
templateId: 517,
entity: null,
recipient: null,
supportCaseId: null,
transactionId: id,
customRecord: null
});
let emailSubject = mergeResult.subject;
let emailBody = mergeResult.body;
emailBody = emailBody.replace("CUSTOMERNAME", customerName);
emailBody = setValuesToEmailTemplate(emailBody, newRec);
if(recepient){
email.send({
author: 58001,
recipients: recepient,
subject: emailSubject,
body: emailBody,
relatedRecords: {
transactionId: id
}
});
log.debug("email sent");
}
//After email sent, set the custom checkbox as false.
record.submitFields({
type: record.Type.CUSTOMER_PAYMENT,
id: id,
values: {
custbody_jj_payment_receipt_flag: false
},
options: {
enableSourcing: false,
ignoreMandatoryFields: true
}
});
}
}
} catch (e) {
log.error("error@beforeLoad", e);
}
}
/**
* Defines the function definition that is executed before record is submitted.
* @param {Object} scriptContext
* @param {Record} scriptContext.newRecord - New record
* @param {Record} scriptContext.oldRecord - Old record
* @param {string} scriptContext.type - Trigger type; use values from the context.UserEventType enum
* @since 2015.2
*/
const beforeSubmit = (scriptContext) => {
try {
let newRec = scriptContext.newRecord;
let recType = newRec.type;
// make the custom checkbox created as true to get the trigger for email sending in before load cotext
if (scriptContext.type == 'create') {
newRec.setValue({
fieldId: 'custbody_jj_payment_receipt_flag',
value: true,
ignoreFieldChange: true
});
}
} catch (e) {
log.error("error@beforeSubmit", e);
}
}
return { beforeLoad, beforeSubmit }
});