This customization is to restrict approval of SO according to the customer’s credit limit
User Event script
/**
* @NApiVersion 2.1
* @NScriptType UserEventScript
*
*
* * VKC Nuts India NS
* VKCN-447: Credit Limit Customization
* ***************************************************************
*
* Author: Jobin and Jismi IT Services
* Date Created: 18/7/2023
* Description: Script used to restrict approval of the Sales orders if the customer does noy have enough credit limit
*
* REVISION HISTORY
* @version 1.0: VKCN-447: Credit Limit Customization Initial build by JJ0131
*/
define(['N/record', 'N/search', 'N/ui/serverWidget'],
(record, search, serverWidget) => {
/**
* 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
* @param {Form} scriptContext.form - Current form
*/
const PENDING_APPROVAL = 'A';
const beforeLoad = (scriptContext) => {
if (scriptContext.type === scriptContext.UserEventType.DELETE) return;
try {
let soForm = scriptContext.form;
let soRecord = scriptContext.newRecord;
let status = soRecord.getValue({
fieldId: 'orderstatus'
});
let soCus = soRecord.getValue({
fieldId: 'entity'
});
let total = soRecord.getValue({
fieldId: 'total'
});
let statusFld = soForm.getField({
id: 'orderstatus'
});
let alert = soForm.addField({
id: 'custpage_alert',
type: serverWidget.FieldType.INLINEHTML,
label: 'Alert'
});
//Alert Box template
const fadingAlertHTML = '<!DOCTYPE html>' +
'<html>' +
'<head>' +
'<title>Fading Floating Alert</title>' +
'<style>' +
'.floating-alert {' +
'position: fixed;' +
'bottom: 20px;' +
'right: 20px;' +
'background-color: #E4287C;' +
'border: 1px solid #ddd;' +
'padding: 20px;' +
'border-radius: 8px;' +
'box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);' +
'font-size: 18px;' +
'color: #FFF;' +
'display: flex;' +
'align-items: center;' +
'animation: fadeInUp 0.5s ease;' +
'transition: opacity 0.5s ease;' +
'}' +
'@keyframes fadeInUp {' +
'from {' +
'opacity: 0;' +
'transform: translateY(10px);' +
'}' +
'to {' +
'opacity: 1;' +
'transform: translateY(0);' +
'}' +
'}' +
'.floating-alert svg {' +
'width: 32px;' +
'height: 32px;' +
'margin-right: 10px;' +
'fill: #FFF;' +
'}' +
'.floating-alert span {' +
'font-weight: bold;' +
'}' +
'</style>' +
'</head>' +
'<body>' +
'<!-- Your other HTML content goes here -->' +
'<script>' +
'function showFadingFloatingAlert() {' +
'const value = \'<div class="floating-alert">\' +' +
'\'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">\'+ ' +
'\'<path d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0zm0 22c-5.523 0-10-4.477-10-10S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-17.172c-.45 0-.828.308-.936.726L11 8.524V16h2V8.524l-.064-.97c-.056-.85-.737-1.51-1.54-1.512zM11 18h2v2h-2v-2z" />\' + ' +
'\'</svg>\' +' +
'\'<span>Insufficient Customer Credit Limit</span>\' +' +
'\'</div>\';' +
'const alertElement = document.createElement(\'div\');' +
'alertElement.innerHTML = value;' +
'document.body.appendChild(alertElement);' +
'setTimeout(() => {' +
'alertElement.style.opacity = \'0\';' +
'setTimeout(() => {' +
'alertElement.remove();' +
'}, 500);' +
'}, 7000);' +
'}' +
'showFadingFloatingAlert();' +
'</script>' +
'</body>' +
'</html>';
statusFld.updateDisplayType({
displayType: serverWidget.FieldDisplayType.DISABLED
});
let hasCreditLimit = creditLimitValidation(total, soCus)
if ((status == PENDING_APPROVAL) && (hasCreditLimit == false)) {
alert.defaultValue = fadingAlertHTML;
}
} catch (e) {
log.error("Error@load", e.message)
}
}
/**
* Defines the function definition that is executed before record is submitted.
* @param {Object} scriptContext
*/
const afterSubmit = (scriptContext) => {
try {
let soRec = scriptContext.newRecord;
let soRecId = soRec.id;
let soCus = soRec.getValue({
fieldId: 'entity'
});
let soTotal = soRec.getValue({
fieldId: 'total'
});
if (scriptContext.type === scriptContext.UserEventType.APPROVE) {
let hasCredLimit = creditLimitValidation(soTotal, soCus)
if (!hasCredLimit) {
record.submitFields({
type: 'salesorder',
id: soRecId,
values: {
'orderstatus': PENDING_APPROVAL
}
});
}
}
} catch (e) {
log.error("Error@afterSubmit", e.message)
}
}
/**
* function to get the total of return authorizations
* @param {string} soCus
*/
function returnAuthSrch(soCus) {
let returnauthorizationSearchObj = search.create({
type: "returnauthorization",
filters:
[
["type", "anyof", "RtnAuth"],
"AND",
["name", "anyof", soCus],
"AND",
["mainline", "is", "T"],
"AND",
["status", "anyof", "RtnAuth:A", "RtnAuth:D", "RtnAuth:B", "RtnAuth:F"],
],
columns:
[
search.createColumn({ name: "amount", label: "Amount" })
]
});
let total = 0;;
returnauthorizationSearchObj.run().each(function (result) {
total += Number(result.getValue({
name: "amount",
label: "Amount"
}))
return true;
});
return total;
}
/**
* Defines the function for validating the credit limit of the customer
* @param {string} soTotal - SO total
* @param {string} soCus - customer ID
*/
function creditLimitValidation(soTotal, soCus) {
let hasCreditLimit = true;
let cusSrch = search.lookupFields({
type: 'customer',
id: soCus,
columns: ['balance', 'unbilledorders', 'creditlimit']
});
let bal = parseFloat(cusSrch.balance);
let unbilledOrders = parseFloat(cusSrch.unbilledorders);
let credLimit = cusSrch.creditlimit;
let returnTotal = returnAuthSrch(soCus);
log.debug("Return Total", returnTotal)
unbilledOrders = (returnTotal * -1) + unbilledOrders
let currentCredLimit = 0;
if (credLimit == "" || credLimit == "0" || credLimit == "0.0" || credLimit == "undefined" || credLimit == null) {
credLimit = 0;
}
currentCredLimit = parseFloat(credLimit) - (bal + unbilledOrders);
log.debug("Credit Limit", currentCredLimit)
log.debug("Total", soTotal)
if (currentCredLimit < 0) {
hasCreditLimit = false;
}
return hasCreditLimit;
}
return { beforeLoad, afterSubmit }
});