Netsuite Email Merge Error (SSS_MERGER_ERROR_OCCURRED) - netsuite

I'm creating a Scheduled SuiteScript in Netsuite that uses the 1.0 version of the API. The goal is to have the script run once per day to send our first time customers an email with their name (or company name) and other pre-formatted content using a Scriptable Email Template.
Once it is up and running, we are planning to extend it with additional functionality, but this is the base that we would like to have running before adding additional code.
The error message is:
SSS_MERGER_ERROR_OCCURRED - Merger error occurred: Unexpected error encountered during merging.
Everything goes smooth up until actually using .merge(). I've added the code below:
function thankyouletter() {
var searchresults = nlapiSearchRecord(null, 'customsearch127'); // minus: , filters
if (searchresults == null) {
response.write('Var searchresults is null.');
return;
} else {
nlapiLogExecution('DEBUG', 'START - Found search results', 'Starting iteration');
for (var i = 0; searchresults != null && i < searchresults.length; i++) {
var searchresult = searchresults[i];
var searchCols = searchresult.getAllColumns();
var internalid = searchresult.getId(); // Will be used after testing is finished
var emailMerger = nlapiCreateEmailMerger(38);
emailMerger.setEntity('customer', 24886); // Set for Testing
var mergeResult = emailMerger.merge(); // Fails and errors here
var emailSubject = mergeResult.getSubject();
var emailBody = mergeResult.getBody();
nlapiSendEmail(nlapiGetUser(), customerid, emailSubject, emailBody, null, null, null, null);
nlapiLogExecution('DEBUG', 'Merge Troubleshooting', 'Just after SendEmail');
}
}
var usageRemaining = context.getRemainingUsage();
nlapiLogExecution('DEBUG', 'usage left => ' + usageRemaining);
nlapiLogExecution('DEBUG', 'Script Finished.', 'Mission Complete');
}
I've removed some of the nlapiLogExecution lines for readability. If anything is confusing or additional info is needed, please let me know and I'll add/fix it.
I've dug through piles of Netsuite's documentation, SuiteAnswers, and web searches trying to find the solution, but the error message is pretty vague.
Any help is greatly appreciated! Thank you.

After further research, I found that the Freemarker Template had a syntax error, and that was causing the error.

Related

Suitescript check if field is searchable

I am trying to search Account data using SuiteScript 1.0 in netsuite, but I am getting "SS_INVALID_SRCH_COL Details: An nlobjSearchColumn contains an invalid column, or is not in proper syntax:" error. I am using Admin role for my login. following the code sample that causing error
function getMasterData(datain)
{
try
{
var recordtype = 'account';
var c;
var strcolumns = nlapiCreateRecord(recordtype).getAllFields();
var searchcolumns = [];
for (c = 0; strcolumns.length && c < strcolumns.length; c += 1)
{
searchcolumns.push(new nlobjSearchColumn(strcolumns[c]));
}
var data = nlapiSearchRecord(recordtype, null, null, searchcolumns);
return data;
}
catch (ex)
{
nlapiLogExecution('debug', 'getMasterData', ex);
}
}
I know the reason: getAllFields() returns all the fields (searchable/nonsearchable) but nlapiSearchRecord works only for searchable fields. My query is how I can filter searchable fields in the all fields returned by getAllFields() function.
Can please anybody help me regarding this. Thanks in advance.
There's no way to know if a field can be used in a search but you can get the supported fields from the Records Browser and create an array with them.
Note that you can get a record with all fields and values using nlapiLoadRecord.

Display of "is not a valid internal id" in Netsuite Suitescript 1.0 when creating a Search on a particular record

I have created a search in Netsuite using Suitescript 1.0 for searching a particular "Account" using its account number. When I save the following script file, an error is being displayed in "filters[0]" line in the code below, where it says "acctnumber is not a valid internal id.". I am new to Netsuite and would want to know why the error is being displayed, and the solution for the same. Below is the following piece of code written in which the error is being occured.
function COGSAcnt() {
var cOGSAcntNumber = '50001';
var acntNo;
var filters = new Array();
filters[0] = new nlobjSearchFilter('acctnumber', null, 'startswith', cOGSAcntNumber);
var columns = new Array();
columns[0] = new nlobjSearchColumn('internalid');
var acntSearch = nlapiSearchRecord('account', null, filters, columns);
if (acntSearch != null) {
for (x=0; x<acntSearch.length; x++) {
acntNo = ITMSearch[x].getValue('internalid');
}
}
nlapiLogExecution('debug', 'acntNo', acntNo);
return acntNo;
}
NOTE: I want the filter to be acctnumber (Account Number), and using that would want to retrieve the internalid of the account in Netsuite.
This is where NS can be a little confusing. If you look at the NS Record browser (http://www.netsuite.com/help/helpcenter/en_US/srbrowser/Browser2016_2/script/record/account.html) look under the Filters section. Account Number (acctnumber) isn't there. However Number (number) is the filter.
Try rewriting the code to use number instead
function COGSAcnt() {
var cOGSAcntNumber = '50001';
var acntNo = [];
var filters = new nlobjSearchFilter('number', null, 'startswith', cOGSAcntNumber);
var acntSearch = nlapiSearchRecord('account', null, filters, columns);
if (acntSearch != null) {
for (x=0; x<acntSearch.length; x++) {
acntNo.push(ITMSearch[x].getId();
}
}
return acntNo;
}

Email template netsuite

I have really big problem, i mean im working in a company where we are using NetSuite as our business platform and now they updated our account with new APIs.
Developer before me wrote this code
for(var k = 0; k < ResultsPPCI.length; k++){
EmployeePPCI = ResultsPPCI[k].getValue('internalid'); //get the value
// log('Internal ID', EmployeePPCI);
// Merge, send, and associate an email with Purchase Order record (id=1000)
var mergeValuesPPCI = {};
mergeValuesPPCI.NLEMPFIRST = nlapiLookupField('employee', EmployeePPCI, 'firstname');
mergeValuesPPCI.NLEMPLAST = nlapiLookupField('employee', EmployeePPCI, 'lastname');
mergeValuesPPCI.NLSUPPLIER = nlapiLookupField('customer', cust, 'companyname');
mergeValuesPPCI.NLPRODUCTCODE = productcodehtml;
var emailBodyPPCI = nlapiMergeRecord(65, 'purchaseorder', poID, null, null, mergeValuesPPCI);
var recordsPPCI = {};
recordsPPCI['transaction'] = poID;
nlapiSendEmail(EmployeePPCI, EmployeePPCI, emailBodyPPCI.getName(), emailBodyPPCI.getValue(), null, null, recordsPPCI);
// log('EmployeePPCI',EmployeePPCI);
nlapiLogExecution('AUDIT', 'Potentional Problem', 'Email Sent');
}
I have problem now because nlapiMergeRecord is deprecated and it wont work. But i really cant find any working example online for hours... The most important part here is actually body of this email that has to be sent. In this case it is productcodehtml or mergeValuesPPCI.NLPRODUCTCODE.
This is how my template looks like :
<p>The QA Release has been submitted by <nlsupplier> for ${transaction.tranId}.</nlsupplier></p>
<p>The following item(s) have a short shelf life:</p>
<nlproductcode></nlproductcode>
Please can you help me with converting this code to new method? How can i connect nlproductcode from template with mergeValuesPPCI.NLPRODUCTCOD from my code?
Thanks in advance!
You can use kotnMergeTemplate as a drop in replacement for nlapiMergeRecord
e.g.:
kotnMergeTemplate(65, 'purchaseorder', poID, null, null, mergeValuesPPCI);
the nlobjEmailMerger does not take custom values so you'd have to post process the results. Again you can look at the example in my script where you'd get the merged string and then run:
var oldCustFieldPatt = /<(nl[^ >]+)>(\s*<\/\1>)?/ig;
content = content.replace(oldCustFieldPatt, function(a,m){
return mergeValuesPPCI[m.toUpperCase()] || mergeValuesPPCI[m.toLowerCase()] || '';
});
You can use the new nlapiCreateEmailMerger(templateId)
First you need to create your email template in netsuite and get the internal id.
Then:
Use nlapiCreateEmailMerger(templateId) to create an nlobjEmailMerger object.
var emailMerger = nlapiCreateEmailMerger(templateId);
Use the nlobjEmailMerger.merge() method to perform the mail merge.
var mergeResult = emailMerger.merge();
Use the nlobjMergeResult methods to obtain the e-mail distribution’s subject and body in string format.
var emailBody = mergeResult.getBody();
Send your email
nlapiSendEmail(senderInternalId, 'receiver#email.com','subject',emailBody, null, null, null);
Good luck!

Netsuite Userevent Script

I have a userevent script to change the Field in Contract record from PO record. The Script is running fine. But whenever I edit a contract record and try to submit it : It throws the error "Another user has updated this record since you began editing it. Please close the record and open it again to make your changes".
May I know the reason behind this ?
/*---------------------------------------------------------------------------------------------------------------
Description : Whenever the PO vendor is changed(due to Split vendor) that should replace the same in Contract page record automatically.
Script type : User Event Script
Script id : customscript452
Version : 1.0
Applied to : Contract
----------------------------------------------------------------------------------------------------------------*/
function srchfield()
{
var stRecordid = nlapiGetRecordId(); //returns the contract id
if(stRecordid== undefined || stRecordid== null || stRecordid==' ')
{
}
else
{
var stRecordtype = nlapiGetRecordType(); //returns the contract record type = jobs
var stRecord = nlapiLoadRecord(nlapiGetRecordType(), stRecordid);
nlapiLogExecution('debug','Load Object',stRecord);
var stContractID = stRecord.getFieldValue('entityid'); //returns the value of the field contractid whose fieldid is = entityid
nlapiLogExecution('debug','stContractID',stContractID);
var stCompanyName = stRecord.getFieldValue('companyname'); //returns the value of the field company name whose fieldid is = companyname
nlapiLogExecution('debug','stCompanyName',stCompanyName);
var stConcatenate = stContractID+" : "+stCompanyName; //Concatenate the two Fields to get the result which needs to be found in PO
var arrFilters = new Array(); // This is Array Filters all the Purchase Order Record Search
arrFilters.push(new nlobjSearchFilter('type', null, 'anyof',
[
'PurchOrd'
]));
arrFilters.push(new nlobjSearchFilter('mainline', null, 'is', 'T')); //This is to exclude line level results
arrFilters.push(new nlobjSearchFilter('custbodycontract', null, 'is', stRecordid)); //This is Filters in Contracts Search
var arrColumns = new Array();
arrColumns.push(new nlobjSearchColumn('entity')); //This is Search Column Field in Records
var arrSearchresults = nlapiSearchRecord('purchaseorder', null, arrFilters, arrColumns); //This is Filters in Search Result Purchase Order
if(arrSearchresults== undefined || arrSearchresults== null || arrSearchresults==' ')
{
}
else
{
var length = arrSearchresults.length;
}
if(length== undefined || length== null || length==' ')
{
}
else
{
for (var i = 0; arrSearchresults != null && i < arrSearchresults.length; i++)
{
var objResult = arrSearchresults[i];
var stRecId = objResult.getId();
var stRecType = objResult.getRecordType();
var stCntrctName = objResult.getValue('entity'); //This is Value are Get Purchase Order Records and Field for Vendor = entity
}
}
//var record = nlapiLoadRecord(nlapiGetRecordType(), stRecordid, stCntrctName);
if (stCntrctName =='custentityranking_vendor_name')
{
}
else
{
var stChangeName = stRecord.setFieldValue('custentityranking_vendor_name', stCntrctName); //This is Value are the Set in Main Vendor Field = custentityranking_vendor_name
nlapiSubmitRecord(stRecord, null, null); // Submit the Field Value in Record Type
}
}
}
The User Event script executes as the Contract record is being saved to the database. At the same time, you are loading a second copy of the record from the database and trying to submit the copy as well. This is causing the error you're seeing.
You fix this by just using nlapiSetFieldValue to set the appropriate field on the Contract.
I might also recommend getting more familiar with JavaScript by going through the JavaScript Guide over at MDN. In particular, take a look at the Boolean description so that you know how JavaScript evaluates Boolean expressions. This will help you greatly reduce the amount of code you've written here, as many of your conditionals are unnecessary.
What userevent do you have? It is happening depending on what type of user event and API you are using. Looking at your code, you are trying to load contract record that is already updated at the database. So you might consider below to address your issue. Hope, it helps.
If it is a before submit, you don't need to load the record where the script is deployed.
Just use nlapiGet* and nlapiSet* to get and set values. You also don't need to use nlapiSubmitRecord to reflect the change. With before submit, it executes before the record is being saved to the database. So your changes will still be reflected.
Then if it is after submit, it will be executed after the record has been saved to the database, Thus you might use the following API depending on your needs. Actually, this is the best practice to make sure the solution .
nlapiGetNewRecord - only use this if the script only needs to retrieve info from header and sublists. And nothing to set.
nlapiLookupField - use this if the script only needs to get value/s at the header and nothing from the line.
nlapiSubmitField - the script don't need to load and submit record if the changes only on header. Just use this API.
nlapiLoadRecord and nlapiSubmitRecord- use the former if the script will have changes at the line and then use the latter api to commit it on the database.
Being a user event script code, The code you showed is very not good considering performance.
Here is the sample you can merge
var stRecordid = nlapiGetRecordId(); //returns the contract id
// Every record has an internal id associated with it. No need to add condition explicitly to check if its null
var stRecordtype = nlapiGetRecordType();
var fields = ['entityid','companyname'];
var columns = nlapiLookupField(stRecordtype, stRecordid, fields);
var stContractID = columns.entityid;
var stCompanyName = columns.companyname;
nlapiLogExecution('debug','stContractID/stCompanyName',stContractID+'/'+stCompanyName);
var stConcatenate = stContractID+" : "+stCompanyName; //Concatenate the two Fields to get the result which needs to be found in PO
//
//your code of search
//you can improve that code also by using nlapilook up
nlapiSubmitField(stRecordtype, stRecordid, 'custentityranking_vendor_name', 'name to be updated');

Creating spam filter for Gmail

I am so sick of getting unwanted mails in my gmail inbox that I am now willing to create a white-list kind of extension which filters all mails coming from people who are not in my contacts list. I searched for this many hours but could not find anything hence thinking of doing this exercise (if it exists, please share the link). I have created 100's of filters but definitely spammers outpace me everytime.
Can someone tell me whether this is possible in first place? I have seen extensions which add functionality in gmail but I don't know how to block an email through an extension. Plz help.
You can setup a whitelist in Gmail but it is unlikely to work for such a large list of addresses. What you can do is create a Google sheet with a list of valid addresses and a Google Script that will scan your inbox against these addresses.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range = sheet.getDataRange();
var values = range.getValues();
var emails = [];
for (var i in values) {
emails.push(values[i][0]);
}
var threads = GmailApp.search("in:inbox is:unread");
for (var i=0; i<threads.length; i++) {
var from = threads[i].getMessages()[0].getFrom();
if ( !emails.indexOf(from) ) {
threads[i].moveToSpam();
}
}
You need to setup a trigger that runs this script every 5 minutes or so.
Thanks a lot Amit for sharing this snippet. With the help of this, I was able to come up with a working solution (ver1.0) and sharing below for others:
function createTriggers() {
ScriptApp.newTrigger('runSpamFilter').timeBased().everyMinutes(10).create();
SpreadsheetApp.getActiveSpreadsheet().toast("The program will check for spam email every 10 minutes and"
+ " send them to Spam Folder after applying label MySpam. You may please close this window.", "Initialized");
}
function removeTriggers(show) {
var triggers = ScriptApp.getScriptTriggers();
for (i=0; i<triggers.length; i++) {
ScriptApp.deleteTrigger(triggers[i]);
}
if (show) {
SpreadsheetApp.getActiveSpreadsheet().toast("The program has stopped.", "Uninstalled");
}
}
function runSpamFilter() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range = sheet.getDataRange();
var values = range.getValues();
var emails = [];
var regex = /([a-zA-Z0-9+._-]+#[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)/gi;
var my_label = GmailApp.getUserLabelByName("MySpam");
var spamCount = 0;
for (var i in values) {
emails.push(values[i][0]);
}
var threads = GmailApp.search("in:inbox is:unread");
for (var i=0; i<threads.length; i++) {
var from = threads[i].getMessages()[0].getFrom();
var from_email = from.match(regex);
if ( emails.indexOf(from_email[0]) == -1 ) {
threads[i].addLabel(my_label);
threads[i].moveToSpam();
spamCount++;
}
}
Logger.log("Spams found = %s", spamCount);
}
function startProgram() {
removeTriggers(false);
createTriggers();
}
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var menu = [
{name: "Step 1: Initialize", functionName: "startProgram"},
{name: "Step 2: Start ", functionName: "runSpamFilter"},
{name: "Uninstall (Stop)", functionName: "removeTriggers"}
];
sheet.addMenu("Gmail Spam Filter v1.0", menu);
}
I may also come up with ver2.0 which removes the current limitation of this script. As of now, you have to make a spreadsheet having all your contacts email addresses. But once you add a new contact, this spreadsheet needs to be updated manually. Hence this script needs an additional trigger which would update the spreadsheet once in say 15 days with the recently added contacts/email addresses. I will share that too later or may be someone can pick from here and come up with ver2.0.
Thanks again.

Resources