Client script
function (currentRecord, search, email, file, record, runtime, dialog, url, https) {
/**
* Function to be executed after page is initialized.
*
* @param {Object} scriptContext
* @param {Record} scriptContext.currentRecord - Current form record
* @param {string} scriptContext.mode - The mode in which the record is being accessed (create, copy, or edit)
*
* @since 2015.2
*/
function pageInit(scriptContext) {
// Initial page setup if needed
}
function sendProposalMail(type, proposalDate, customerId, customerName) {
let getRecord = currentRecord.get();
let estimateId = getRecord.id;
console.log('Proposal Date:', proposalDate);
// Check if customer is inactive
if (customerId && isCustomerInactive(customerId)) {
alert('This transaction uses the following inactive entity. We recommend to only do transactions with active entities.nn' + customerName);
// return;
}
// Resolve Suitelet URL and redirect user to the Suitelet
let suiteletUrl = url.resolveScript({
scriptId: 'customscript_jj_sl_fetch_files_cdus4259',
deploymentId: 'customdeploy_jj_sl_fetch_files_cdus4259',
params: {
estimateId: estimateId
}
});
let confirmationMessage = proposalDate ? "Email has already been sent on: " + proposalDate + ". Do you want to resend it?" : "Are you sure you want to send the proposal email?";
log.debug('confirmationMessage', confirmationMessage);
// Confirmation dialog options
var options = {
title: "Send Proposal Email",
message: confirmationMessage,
buttons: [
{ label: 'Yes', value: true },
{ label: 'Cancel', value: false }
]
};
function openSuitelet() {
dialog.create({
title: 'Send Proposal Email',
message: '<iframe src="' + suiteletUrl + '" width="100%" height="112px" frameborder="0"></iframe>'
,
buttons: [
{ label: 'Cancel', value: 'cancel' }
]
}).then(function (result) {
console.log('Success with value (OpenSuitelet): ' + result);
}).catch(function (failure) {
console.log('Failure OpenSuitelet: ' + failure);
});
}
// dialog.confirm(options)
dialog.create(options)
.then(function (result) {
console.log("User clicked OK: ", result);
if(result == true)
{
openSuitelet(); // Open Suitelet only on confirmation
}
else
{
return;
}
})
.catch(function (failure) {
console.log("User clicked Cancel: ", failure);
// No further action taken if "Cancel" is clicked
});
// Redirect to Suitelet to display the form to a new page
// window.open(suiteletUrl, '_blank');
}
function isCustomerInactive(customerId) {
try {
let searchResult = search.lookupFields({
type: search.Type.CUSTOMER,
id: customerId,
columns: ['isinactive']
});
let customerInactive = searchResult.isinactive;
console.log("customerInactive", customerInactive);
return customerInactive;
}
catch (e) {
log.error("Error@isCustomerInactive", e);
}
}
return {
pageInit: pageInit,
sendProposalMail: sendProposalMail
};
});
Suitelet script
(email, file, record, search, serverWidget) => {
/**
* Defines the Suitelet script trigger point.
* @param {Object} scriptContext
* @param {ServerRequest} scriptContext.request - Incoming request
* @param {ServerResponse} scriptContext.response - Suitelet response
* @since 2015.2
*/
const onRequest = (scriptContext) => {
try {
let estimateId;
if (scriptContext.request.method === 'GET') {
estimateId = scriptContext.request.parameters.estimateId;
log.debug("estimateId get", estimateId);
// Write custom HTML form
let htmlForm = `
<html>
<head>
<style>
body {
font-family: sans-serif;
padding: 2px;
margin: 0;
overflow: hidden;
}
.form-container {
max-width: 500px;
margin: 0 auto;
padding: 5px;
box-sizing: border-box;
}
.form-container input[type="text"] {
width: 100%;
padding: 5px;
margin: 5px 0;
border: 1px solid #ccc;
border-radius: 5px;
box-sizing: border-box;
}
.form-container input[type="submit"] {
padding: 5px 10px;
background-color: #E0E0E0;
color: black;
border: none;
border-radius: 5px;
cursor: pointer;
font-weight: bold;
}
.form-container input[type="submit"]:hover {
background-color: #D3D3D3;
}
</style>
</head>
<body>
<div class="form-container">
<form method="post">
<label for="email">Enter Email Address</label><br>
<input type="text" id="email" name="email" required><br><br>
<input type="submit" value="Send Email">
</form>
</div>
</body>
</html>
`;
scriptContext.response.write(htmlForm);
}
else if (scriptContext.request.method === 'POST') {
let estimateId = scriptContext.request.parameters.estimateId;
let emailAddresses = scriptContext.request.parameters.email;
let emailAddressesInput = emailAddresses.split(',').map(email => email.trim());
// Validate: Ensure no more than 8 email addresses and valid format
if (emailAddressesInput.length > 8) {
scriptContext.response.write('Error: Please enter no more than 8 email addresses.');
return;
}
// Separate valid and invalid email addresses
let validEmails = [];
let invalidEmails = [];
// Validate email format and categorize
emailAddressesInput.forEach(email => {
if (validateEmail(email)) {
validEmails.push(email);
} else {
invalidEmails.push(email);
}
});
// If all emails are invalid, return an error message
if (validEmails.length === 0) {
scriptContext.response.write('Error: All provided email addresses are invalid: ' + invalidEmails.join(', '));
return;
}
// Log invalid emails, but proceed with sending to valid ones
if (invalidEmails.length > 0) {
log.error('Invalid Emails', 'The following email addresses are invalid: ' + invalidEmails.join(', '));
scriptContext.response.write('<b>Warning:</b> The following email address(es) are invalid and were not included: ' + invalidEmails.join(', '));
}
let attachedFilesLinks = getAttachedFilesLinks(estimateId);
log.debug("attachedFilesLinks", attachedFilesLinks);
if (!attachedFilesLinks) { // New check for empty file links
scriptContext.response.write('Alert: No files are attached to the estimate. Email not sent.');
return;
}
let estimateDetails = getEstimateDetails(estimateId);
log.debug("estimateDetails", estimateDetails);
let subject = 'Estimate #' + estimateDetails.estimateNumber;
let body = 'Thank you. Please let me know if I can assist you any further. <br><br>Attachments:<br>' + attachedFilesLinks;
let ccEmails = (estimateDetails.customerCSR) ? [estimateDetails.customerCSR, 8772] : [8772];
// Handle email sending logic here
try {
email.send({
author: 2228,
recipients: validEmails, // Only valid emails are used
subject: subject,
body: body,
cc: ccEmails,
relatedRecords: {
transactionId: estimateId
}
});
updateEmailSentDates(estimateId);
scriptContext.response.write('<br><br>Email sent to: ' + validEmails.join(', '));
} catch (emailError) {
log.error("Email Sending Error", emailError);
scriptContext.response.write('Failed to send email: ' + emailError.message);
}
}
}
catch (e) {
log.error("Error@onRequest", e);
scriptContext.response.write('An error occurred: ' + e.message);
}
}
function validateEmail(email) {
try {
let re = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/;
return re.test(String(email).toLowerCase());
}
catch (e) {
log.error('Error@validateEmail', e);
}
}
function getEstimateDetails(estimateId) {
try {
let searchResult = search.lookupFields({
type: search.Type.ESTIMATE,
id: estimateId,
columns: ['custbody_jj_proposal_lastsent_cdus4259', 'customer.custentity_jj_customerservicerep', 'entity', 'tranid']
});
log.debug("entity", searchResult.entity[0].value);
log.debug("Doc Num", searchResult.tranid);
return {
emailSentDate: searchResult.custbody_jj_proposal_lastsent_cdus4259,
customerCSR: searchResult['customer.custentity_jj_customerservicerep']?.[0]?.value || null,
emailRecipient: searchResult.entity[0].value,
estimateNumber: searchResult.tranid || 'No estimate number'
};
} catch (e) {
log.error("Error@getEstimateDetails", e);
return null;
}
}
function getAttachedFilesLinks(estimateId) {
try {
let fileData = getAttachedFiles(estimateId);
if (fileData.length === 0) {
return '';
}
return fileData.map(function (file) {
//search lookup isonline field
let isOnlineLookup = search.lookupFields({
type: search.Type.ESTIMATE,
id: estimateId,
columns: ['file.availablewithoutlogin']
});
let isOnline = isOnlineLookup['file.availablewithoutlogin'];
// log.debug("isonline after if", isOnline);
if (!isOnline) {
// location.reload();
record.load({
type: record.Type.ESTIMATE,
id: estimateId
});
}
let baseUrl = 'https://5272043-sb1.app.netsuite.com';
return '<a href="' + baseUrl + file.url + '" target="_blank">' + file.name + '</a>';
}).join('<br>');
} catch (e) {
log.error("Error@getAttachedFilesLinks", e);
return '';
}
}
function getAttachedFiles(estimateId) {
try {
let estimateSearchObj = search.create({
type: search.Type.ESTIMATE,
filters: [
["mainline", "is", "T"],
"AND",
["internalid", "anyof", estimateId]
],
columns: [
search.createColumn({ name: "url", join: "file" }),
search.createColumn({ name: "internalid", join: "file" }),
search.createColumn({ name: "name", join: "file" }),
search.createColumn({ name: "availablewithoutlogin", join: "file" })
]
});
let fileData = [];
estimateSearchObj.run().each(function (result) {
let fileId = result.getValue({ name: "internalid", join: "file" });
if (fileId) {
fileData.push({
id: fileId,
url: result.getValue({ name: "url", join: "file" }),
name: result.getValue({ name: "name", join: "file" }),
isOnline: result.getValue({ name: "availablewithoutlogin", join: "file" }) === 'T'
});
}
return true;
});
return fileData;
} catch (e) {
log.error("Error@getAttachedFiles", e);
return [];
}
}
function updateEmailSentDates(estimateId) {
try {
record.submitFields({
type: record.Type.ESTIMATE,
id: estimateId,
values: {
custbody_jj_proposal_lastsent_cdus4259: new Date().toISOString().split('T')[0]
}
});
} catch (e) {
log.error("Error@updateEmailSentDates", e);
}
}
function isEmpty(stValue) {
return !stValue || (Array.isArray(stValue) && stValue.length === 0) || (typeof stValue === 'object' && Object.keys(stValue).length === 0);
}
return { onRequest }
});