Tip 26 – Get Nexus list attached to Subsidiary record in Netsuite using SuiteScript

I got a requirement to fetch nexus list attached to a subsidiary record.

When I check Record Browser, I don’t see any list ID for Nexus.

After trying for some hours, I found the exact sublist ID & column internal ID to fetch nexus value. Here it is:

var selectedNexus = nlapiGetLineItemValue(“nexus”, “nexusid”, 1);

nexus” : Sublist ID

nexusid” : Column ID

I believe, when you get such requirement, this tip will help you.

 

Happy Coding 🙂

Advertisements

Tip 25 – Pass filters to Map/Reduce script from Suitelet OR Call Map/Reduced script from Suitelet in NetSuite

One of my friend asked me, if we can pass filter/search result to Map/Reduce script from Suitelet. If yes, how?

Here is my tip on the same.

Requirement: A custom UI with 2 fields and a submit Button.

Fields:

  1. Date
  2. Subsidiary

On selection of Date and Subsidiary, on submit, all In Progress Projects are to be picked and project calculated amount to be considered as amount in new Journal Entry record created against each Project.

Solution:

Create Suitelet script to have custom UI page SS_Project_Process_UI.js

/**
* @NApiVersion 2.x
* @NScriptType Suitelet
* @NModuleScope Public
*/
define([‘N/ui/serverWidget’, ‘N/search’, ‘N/record’, ‘N/redirect’, ‘N/runtime’, ‘N/task’, ‘N/email’],
function(serverWidget, search, record, redirect, runtime, task, email) {
function CreateProjectProcessPage(context) {
try{
if(context.request.method ==’GET’){
var form = serverWidget.createForm({ title: ‘Generate JE against Projects’,       hideNavBar : false});
form.addField({
id: “custpage_date”,
type: serverWidget.FieldType.DATE,
label: “Select Date”
});
form.addField({
id: “custpage_subsidiary”,
type: serverWidget.FieldType.MULTISELEC​T,
label: “Subsidiary”,
source: “subsidiary”
});
form.addSubmitButton(“Create Journal Entry”);
context.response.writePage(form);
}
else{//POST

var delimiter = /\u0005/;
var date = context.request.parameters.custpage_date;
var subsidiary = (context.request.parameters.custpage_subsidiary)?                      context.request.parameters.custpage_subsidiary.split(delimiter):””;

//Send filters as object using a parameter
var objparam = JSON.stringify({“date”: date, “subsidiary”: subsidiary});

//Create task to call Map/Reduced
var mrTask = task.create({
taskType: task.TaskType.MAP_REDUCE,
scriptId: “customscript_mrs_project_to_je”,
deploymentId: “customdeploy_mrs_project_to_je”,
params: {“custscript_filter_parameter”: objparam}
});
var mrTaskId = mrTask.submit();
var taskStatus = task.checkStatus(mrTaskId);
log.debug(“taskStatus”, taskStatus);

if (taskStatus.status === ‘FAILED’) {
//Send Email or Do as required
}
}
}catch(ex){
log.error(“Error in Suitelet”,JSON.stringify(ex));
}
}

return {
onRequest: CreateProjectProcessPage
};});

 

Now create a Map/Reduced Script MRS_Generate_JE_From_Project.js:

/**
* @NApiVersion 2.0
* @NScriptType MapReduceScript
*/
define([‘N/record’, ‘N/search’, ‘N/runtime’, ‘N/email’],
function(record, search, runtime, email){

var SAVED_SEARCH_ID = ‘customsearch_project_list’;
var ACC_CREDIT = 54;
var ACC_DEBIT = 142;
var ACC_DEFFERED = 1023;
var garrJEInfo = new Array();

function getInputData(){
try{
//want to capture time started
var filters = runtime.getCurrentScript().getParameter({name: ‘custscript_filter_parameter’});
var jsonData = JSON.parse(filters);
log.debug(‘Map/Reduced jsonData’, jsonData);
var date = jsonData.date;
var subsidiary = jsonData.subsidiary;
var searchObj = search.load({ id : “customsearch_project_list”});
if(date){
var MyFilters = search.createFilter({name: ‘startdate’, operator: ‘onorbefore’, values: date });
searchObj.filters.push(MyFilters);
}
if(subsidiary){
var MyFilters = search.createFilter({name: ‘subsidiary’, operator: ‘anyof’, values: subsidiary });
searchObj.filters.push(MyFilters);
}
return searchObj;
}catch(ex){
log.error(‘getInputData error: ‘, ex.message);
}
}
function map(context){
var searchResult = JSON.parse(context.value);
// Note: You can use passed parameter from Suitelet throughout events in Map/Reduced Script wherever you need
var filters = runtime.getCurrentScript().getParameter({name: ‘custscript_filter_parameter’});
var jsonData = JSON.parse(filters);
var date = jsonData.date; //Use date wherever required

//Rest of code to get Project Details and Create JE against each search result which has Project details

}

function summarize(){

//Add code for summary details

}

return{
getInputData: getInputData,
map: map,
summarize: summarize
};
});

 

Hope now you must have got some idea on passing paremeters from Suitelet to Map/Reduced script.

 

Happy Coding 🙂

 

 

Tip 24 – Get Input with Processed record IDs in Map event in Summary event in Map/Reduce script SuiteScript 2.0 in NetSuite

Recently I got a very challenging requirement which I want to share with you, so that you can use in your project whenever required.

Requirement:

Use a particular search to get All In Progress Projects and create JE record against each Project and send an summary email which will show list of Project IDs and New JEs created against each Project.

Solution:

To accomplish this task, I have used Map/Reduce script which will include 3 functions :

  1. getInputData()
  2. map()
  3. summarize()

getInputData(): This function will be responsible to fetch all search results

map(): This function will execute each project to create new Journal Entry record using project ID

summerize(): This function will be used to get summary of whole script and send email.

/**
* @NApiVersion 2.0
* @NScriptType MapReduceScript
*/
define([‘N/record’, ‘N/search’, ‘N/runtime’, ‘N/email’],
function(record, search, runtime, email){

function getInputData(){
var searchObj = search.load({
id : “customsearch_project_list”
});
return searchObj;
}

function map(context){
var searchResult = JSON.parse(context.value);
var projectId = searchResult.id;  //Get Project ID
if(projectId){
var newJE = CreateJournal(projectId);//Method to create JE using Project ID

}
if(newJE){

//This will add comments to context which will be fetched in summary
                 context.write(projectId, newJE); //Key-Value pair
}
}

function summarize(summary){
var subject = “Summary Email: Project to JE creation process”;
var message = “JE creation against Projects is just processed.\n\n”;
var userid = runtime.getCurrentUser().id;
var contents = ”;

  //Use summary.output which will contain list of key-value pair that we have entered at end of map() function
summary.output.iterator().each(function(key, value) {
               contents += “New JE created(ID ” + value + “) against Project(ID “+ key +”) \n\n”;
               return true;
       });
message += contents;
email.send({author: userid,
recipients: “<recipient email id>”,
subject: subject,
body: message
});
log.debug(‘——-SCRIPT——– ‘, ‘—-END——-‘);
}
return{
getInputData: getInputData,
map: map,
summarize: summarize
};
});

summaryemail
Fig 1: Summary email including input ID and processed new record ID

Note: We can use “context.write()” to pass any information from map() to summarize().

This tip is helpful for Netsuite developers who are working on Map/Reduce script SuiteScript 2.0.

 

Happy Coding 🙂

Tip 23 – Get Multiselect Field Value in On Suitelet POST in SuiteScript 2.0 in NetSuite

We all know, how to get a Select field value on Submit of Suitelet Page in SuiteScript 2.0.

Sample Code to Get Select Field Value:

function CreateRevRecPage(context) {
if(context.request.method ==’GET’){
var form = serverWidget.createForm({
title: ‘Sample Suitelet Page’,
hideNavBar : false
});
var dateFld = form.addField({
id: “custpage_account”,
type: serverWidget.FieldType.SELECT,
label: “Select Account”
});

form.addField({
id: “custpage_subsidiary”,
type: serverWidget.FieldType.MULTISELEC​T,
label: “Subsidiary”,
source: “subsidiary”
});
form.addSubmitButton(“Submit”);
context.response.writePage(form);
}
else{//POST
//Get Select Field Value
var account = context.request.parameters.custpage_account;
}
}

 

Multiselect_1
Fig 1: Suitelet page holds Multiselect Subsidiary field

But How to get multiselect field value on Submit?

If you will use similar code like for Select Field, you will get all values as a string.

Like let’s say if you have selected 6 subsidiaries in a Subsidiary Multiselect Field whose internal IDs are 10, 12, 16, 19, 23, 26.

var subsidiaries= context.request.parameters.custpage_subsidiary;

Above code will return value as 101216192326, which is very confusing.

So, to get list of subsidiaries as array of Subsidiary list, please use below code:

var delimiter = /\u0005/;
var subsidiaries =context.request.parameters.custpage_subsidiary.split(delimiter);
log.debug(“subsidiary”, subsidiary);

Output of above code will be : [“10”, “12”, “16”, “19”, “23”, “26”].

Multiselect_2
Fig 2: Subsidiary List in parameter

Note: To get MultiSelect field value use Delimiter /\u0005/.

 

Hope this small tip will help you.

 

Happy Coding 🙂

Tip 22: Get Record ID from a Saved Search GroupBy Results in Map/Reduce Script in NetSuite Suitescript 2.0

Hello Friends,

Sorry for delay in posting next tip.

Here is one for you hoping this will help you.

This tip will be helpful if you are working in SuiteScript 2.0.

Believing you have prior knowledge of SuiteScript 2.0 & Map/Reduce script:

In Map/Reduce script, usually we send Search Resultset to Map(context).

And inside Map(), we get Record ID by using code:

var recordId = searchResult.id;

But what if, your search runs by summary??

Below is a sample script code that will help you to run Map/Reduce script based on Summary search.

Requirement: I want to generate JE against each In progress Project including Amounts grouped by Invoices of each Project.

Step 1: Create Saved Search:

My 1st step will be to create a saved search on project record

Record Type: Project

Status: In progress

Results:

  1. Field: Internal ID, Summary: GROUP
  2. Field: Name, Summary: GROUP
  3. Field: Transaction: Amount, Summary: SUM

Internal ID of the saved search: ‘customsearch_project_list’

Step 2: Create Map/Reduce Script:

Create Map/Reduce script with file name MRS_Create_JE_Against_Projects.js

Sample Code for MRS_Create_JE_Against_Projects.js:

/**
* @NApiVersion 2.0
* @NScriptType MapReduceScript
*/
define([‘N/record’, ‘N/search’, ‘N/runtime’, ‘N/email’],

/**
* This script will search all Project with Sum of Amounts of Invoice related to project and creates JE against each project
*/
function(record, search, runtime, email){

var SAVED_SEARCH_ID = ‘customsearch_project_list’;
var ACC_CREDIT = “<Credit Account ID>”;
var ACC_DEBIT = “<Debit Account ID>”;

/**
* The following function is required for map/reduce scripts. It is called once as the entry point of the script.
* This function will search search all Project with Sum of Amounts of Invoice related to project and creates JE against each project
* @param {Object} context : context object
* @governance 0 units
*/
function getInputData(){
try{
var searchObj = search.load({
id : “customsearch_project_list”
});
var arrResults = new Array();
var id = “”, amount = 0.00;

//Run each result and get ID and push it to array
searchObj.run().each(function(result) {
id = “”;
id = result.getValue({name: “internalid”, summary: “GROUP”});

amount = result.getValue({name: “amount”, join: “transaction” summary: “SUM”});
arrResults.push({“id”: id, “amount”: amount});
});
//Return array of IDs-Amount which will be passed to Map() function instead of Search Object
return arrResults;
}catch(ex){
log.error(‘getInputData error: ‘, ex.message);
}
}

/**
* The following function is required for map/reduce scripts. It is called for each key/value pair in the getInputData function.
* For each Project record found in search result, get ID & Amount and create JE against each project record
* @param {Object} context : context object
*/
function map(context){
try{
var searchResult = JSON.parse(context.value);
//Get Project ID and its Amount
  var projectId = searchResult.id;

              var amount = searchResult.amount;
log.debug(“MAP Project IS – Amount”,  projectId + ” – ” + amount);
if(projectId){
//Add code to create JE against this Project ID
}
}catch(ex){
log.error(“Error in Map”, JSON.stringify(ex));
}
}
return{
getInputData: getInputData,
map: map
};
});

Now your script is ready. Upload this file and create Map/Reduce script. Now deploy and execute this script and see the result.

As you see here, we are not returning search object in getInputDate() unlike general Map/Reduce script.

Instead we are sending array of objects which hold IDs and Sum of Invoice Amounts to map() function for next execution.

 

Hope that helps you.

Also, let me know, if you want any tips on Map/Reduce on specific requirement.

I will add it, is I have any expertise on that.

 

Happy Coding 🙂

 

Tip 21: Does your record.save() take more time then expected?? Try this….

Recently while working on a Map/Reduced script, I had to load record set some body and line item fields and save the record for each result in “map” section.

Surprisingly while every other code was behaving as usual, but I had to wait long to get each record saved. I repeated my script to execute multiple times to see why is it so….but each time, I was getting the same problem.

Then I checked my code. It was a simple one line code:

var updateId = soRecord.save();
log.debug(“Sales Order updated”, updateId);

Can anyone guess, what may be the problem??

I just tried a simple thing thinking lets try below code

var updateId = soRecord.save({
enableSourcing: true,
ignoreMandatoryFields: true
});
log.debug(“Sales Order updated”, updateId);

AND I WAS RIGHT !!!!!!!!!!!!!!!

It worked…..

I don’t know what’s the logic behind it….but it worked… 🙂

Friends, if any of you having similar issues…try this and let me know if that helped or if that didn’t work for you….I will try something else ;P

Anyways…I will be happy if anybody can tell me what can be the reason behind it…. will be waiting for your reply 🙂

 

 

Tip 20: How can I use a search on Document(file) type in Netsuite SuiteScript (1.0 & 2.0)

Hello Friend,

Its always advised to avoid using saved searches  in scripting as there may be a chance of getting deleted either knowingly/unknowingly.

But there are certain searches which are not included in Search.Type like below example:

While working on a requirement, I realized to add a search on File. As I suggested above I always prefer to use search created in script rather than using a saved search.

But unfortunately I didn’t find any Search.Type for File/Document Type.

So below code was showing error Type Document is not a Valid type in Search.

var mySearch = search.create({
                type: Search.Type.DOCUMENT,
                filters : myFilters,
                columns : myColumns
            });

 

Then I checked in Saved Search and Document type is available there.

Not sure why Netsuite has that difference in Search.Type and Saved Search Type.

Then I created a saved search using Document type and loaded that search in my script as I have no other option.

saved_seach
Fig 1 – Saved Search On Document Record

I recommend to use word like “(Used By Script)” or “(DO NOT DELETE)” in saved search name, so that if somebody will see that saved search, he will be able to understand, this saved search is used for some script. So risk of getting deleted will be reduced to somewhat.

So, here a simple suggestion to all who has similar issues:

If something is not happening using search.create(), try creating saved search on same search type and use search.load() and use that as search.load() doesn’t need any type parameter.

           //Load Saved Search created already with selected id as filter
            var mySearch = search.load({
                id: ‘customsearch_search_file’
            });

I have used SuietScript 2.0 in my sample code. You can refer to SuiteScript 1.0 accordingly if you need.

Hope that will help you.

Happy Coding 🙂

Thanks,

Asha