What's the "official" way to pass variables in SS2.0? - netsuite

Run into a kind of silly problem.
I want to pass a variable between stages on a map/reduce script. Is it there an "official" or best way to do this (rather than sending it with the returned results).
This is my last approach:
/**
* #NApiVersion 2.0
* #NScriptType MapReduceScript
*/
define(["N/search", "N/record", "N/email", "N/runtime", "N/task", "/SuiteScripts/Libraries/tools_lib"],
function (search, record, email, runtime, task, tools) {
var ss = runtime.getCurrentSession();
var conf = {};
/**
* Retrieve the CSV contents and return as an object
* #return {Array|*}
*/
function getInputData() {
log.debug("setting", "foo");
ss.set({name: "foo", value: "bar"});
//Session
var foo = ss.get({name: "foo"});
log.debug("foo 1", foo);
//var pass
conf["foo"] = "bar";
return [1, 2, 3];
}
/**
* Search and group by type, all records with matching entries on the CSV field
* #param context
* #return {boolean}
*/
function map(context) {
//Session
var foo = ss.get({name: "foo"});
log.debug("foo 2", foo);
//Var pass
log.debug("foo 3", conf["foo"]);
return false;
}
foo 1 = bar
foo2 = null
foo3 = null

NetSuite stores whatever you return from the previous phase in context.value.
No matter what data type you return, it will always get sent to the next phase as a String, so you need to JSON.parse it if you want to work with a different data type.
function getInputData() {
return [1,2,3];
}
function map(context) {
log.debug(JSON.parse(context.value));
}
You cannot get access to specific variables from previous phases. If you want to pass data along, you need to build an appropriate data structure with the values you want to pass and return it, then parse it out of context.value in the subsequent phase.

This is coming a little late, but one solution/workaround I found is using a Scheduled Script to call a Map/Reduce Script.
You can dynamically set script parameters within the Scheduled Script when you initiate the map/reduce script (using the 'N/task' module).

Related

On an afterSubmit when we creating a copy of one inventory item (name with '-c') ,The original ID of item link should come in a field on a copy order

I tried this above, here I am getting a null value only from my previous record.
Kindly give some guidance to solve my questions.
thanks in advance.
/**
*#NApiVersion 2.0
*#NScriptType UserEventScript
*/
define(["N/url", "N/record", "N/runtime"], function (url, record, runtime) {
function afterSubmit(context){
var recordobj = context.newRecord;
var prevItemrecord= context.oldRecord;
var Itemname = recordobj.getValue({fieldId:'itemid'});
var prevItemname = prevItemrecord.getValue({fieldId : 'itemid'});
var Type=context.type;
var checkbox=recordobj.getValue({fieldId:'custitem17'});
if(Type== context.UserEventType.CREATE)
if((Itemname=prevItemname+'-c')&&(checkbox=true))
record.submitFields({
type: recordobj.type,
id: recordobj.id,
values:{custitem_item_link:prevItemname}
});
}
return{
afterSubmit:afterSubmit
}
});
This is my code
On create there is no old record.
Since you are trying to update the same record as was just created you are better off having this in a beforeSubmit event script
if((Itemname=prevItemname+'-c')&&(checkbox=true)) this is an error
if((Itemname == prevItemname+'-c') && checkbox) is more what you need
If you are trying to capture a copy operation you can set that up in the beforeLoad event that you use in the beforeSubmit event.
function beforeLoad(ctx){
if(ctx.type == ctx.UserEventType.COPY){
if(ctx.form){
ctx.form.addField({
id:'custpage_original_item',
label:'Copied Item',
type:ui.FieldType.SELECT,
source:'item'
}).updateDisplayType({
displayType:ui.FieldDisplayType.HIDDEN
}).defaultValue = ctx.request.parameters.id;
// your naming makes me wonder if you are trying to link to the
// source item rather than just saving a reference to the source
// item's name
/*
* Using the original item's name like below is closer to what you
* posted but I think by the time the script runs the itemid field
* has been cleared.
* ctx.form.addField({
* id:'custpage_original_item_name',
* label:'Copied Item Name',
* type:ui.FieldType.TEXT
* }).updateDisplayType({
* displayType:ui.FieldDisplayType.HIDDEN
* }).defaultValue = ctx.newRecord.getValue({fieldId:'itemid'});
*/
}
}
}
function beforeSubmit(ctx){
if(ctx.type == ctx.UserEventType.CREATE){
const itemRec = ctx.newRecord;
if(itemRec.getValue({fieldId:'custitem17'})){
const sourceId = itemRec.getValue({fieldId:'custpage_original_item'})
if(sourceId){
itemRec.setValue({
fieldId:'custitem_item_link:prevItemname',
value:sourceId
});
/* or use a search function to look up the original item's name
* and then test the new item's name.
*/
}
}
}
}

Get list of version ids for a blob using node.js

I am trying to get list of version ids for a blob using node.js.
async function getBlobNameAndVersions() {
for await (const blob of containerClient.listBlobsFlat(includeVersions?: true)) {
const tempBlockBlobClient = containerClient.getBlockBlobClient(blob.name);
console.log(`\n\tname: ${blob.versionId}\n\tURL: ${tempBlockBlobClient.name}\n`);
}
}
getBlobNameAndVersions()
Received error saying "ReferenceError: inlcudeVersions is not defined"
When looked at the reference listBlobsFlat, below were options to include versions.
listBlobsFlat(options?: ContainerListBlobsOptions): PagedAsyncIterableIterator<BlobItem, ContainerListBlobFlatSegmentResponse>;
/**
* Returns an AsyncIterableIterator for ContainerListBlobHierarchySegmentResponse
*
* #param delimiter - The character or string used to define the virtual hierarchy
* #param marker - A string value that identifies the portion of
* the list of blobs to be returned with the next listing operation. The
* operation returns the ContinuationToken value within the response body if the
* listing operation did not return all blobs remaining to be listed
* with the current page. The ContinuationToken value can be used as the value for
* the marker parameter in a subsequent call to request the next page of list
* items. The marker value is opaque to the client.
* #param options - Options to list blobs operation.
Tried using options as below.
export declare interface ContainerListBlobsOptions extends CommonOptions {
* Specifies whether versions should be included in the enumeration. Versions are listed from oldest to newest in the response.
*/
includeVersions?: boolean;
}
You would need to pass the options as an object i.e. instead of passing includeVersions?: true, you would need to use {includeVersions: true}.
So your code would be something like:
async function getBlobNameAndVersions() {
for await (const blob of containerClient.listBlobsFlat({includeVersions: true})) {
const tempBlockBlobClient = containerClient.getBlockBlobClient(blob.name);
console.log(`\n\tname: ${blob.name}\n\tURL: ${tempBlockBlobClient.url}\n`);
}
}
Updated Code is working to get version ids of blobs and its URL's
async function getBlobNameAndVersions() {
for await (const blob of containerClient.listBlobsFlat({includeVersions: true})){
const tempBlockBlobClient = containerClient.getBlockBlobClient(blob.name);
console.log(`\n\tversion: ${blob.versionId}\n\tURL: ${tempBlockBlobClient.url}\n`);
}
}

"SuiteScript 2.0 entry point scripts must implement one script type function" Error

I'm trying to upload this code to NetSuite
/**
* #NApiVersion 2.0
* #NScriptType ClientScript
* #NModuleScope SameAccount
*/
define(['N/ui/dialog'],
function(dialog){
/**
* Validation function to be executed when sublist line is committed.
*
* #param {Object} context
* #param {Record} context.currentRecord - Current form record
* #param {string} context.sublistId - Sublist name
*
* #returns {boolean} Return true if sublist line is valid
*
* #since 2015.2
*/
function validadeRate(context){
try{
var currentRecord = context.currentRecord
var sublistName = context.sublistId
if(sublistname ==='expense'){
var categ = CurrentRecord.getCurrentSublistValue({
sublistId: sublistName,
fieldId: 'category'
})
if ((categ = 259) && (rate != 0.819)){
var currIndex = currentRecord.getCurrentSublistIndex({
sublistId: sublistName
})
currIndex +=1
var options = {
title : 'Rate Incorreto!',
message:'Por favor, verifique o valor informado no campo Rate na linha ' + currIndex + '.',
}
dialog.alert(options).then(function (result) { }).catch(function(result){})
return false
}
}
return true
}
catch(ex){
log.error('validateLine: ', ex.message)
}
}
return {
validadeRate : validadeRate
}
});
But I'm getting this error when I'm trying to upload to file to Netsuite:
Notice
SuiteScript 2.0 entry point scripts must implement one script type function.*
This is part of a function that will validade the rate for one expense category.
How can I solve this?
thanks in advance!
This is NetSuite's 'Entry Point Script Validation' saying that the script is invalid because it doesn't include one of the predefined entry point (event) functions. These functions are:
fieldChanged
lineInit
pageInit
postSourcing
saveRecord
sublistChanged
validateDelete
validateField
validateInsert
validateLine
You can work around this validation and upload the script by adding one of those entry points, even if it does nothing. For example, inside your function (dialog) function you can add a pageInit() function:
function pageInit(scriptContext) {}
and change your return block to:
return {
validadeRate : validadeRate,
pageInit: pageInit
}
Now it has a valid entry point and the validation should pass.
However, there may be an even easier way. It appears (going by the JSDoc block), that your validadeRate function is supposed to be triggered each time a sublist line is added. This is exactly what the validateLine entry point is for. So you could just change the key in your return block to "validateLine"
return {
validateLine: validadeRate
}
and NetSuite would know to call validadeRate each time a line is added.
You have specified this as a Client Script module, but have not assigned a handler to any of the Client Script entry points. Read the Help document SuiteScript 2.0 Client Script Entry Points and API, and implement any one of the entry points in your module.
Change return function as below. and test once.
return
{
validateLine : validadeRate
}

How to retry a request until i get a valid dynamically generated value in response using karate dsl

I am sending a request to fetch the API ID from backend but because my backend is slow it doesn't give back the ID in one go and that's making my test case fail in the first try. Though it passes if I try again, but that's not ideally it should work. I tried putting a sleep, but that doesn't look promising either.
My test case is :
Given URL storeURL
And param query =
When method get
Then status 200
call read('Sleep.feature')
def APIIDStr = response.list[0].id
print 'APIID from Store is: ', APIIDStr
Can i do something here so that if APIIDStr is empty in the first go , it tries to fetch again until it gets a valid value?
Yes. Please refer to the documentation on how to implement polling using JavaScript: https://github.com/intuit/karate#polling
function(x) {
while (true) {
var result = karate.call('get.feature');
var greeting = result.response;
karate.log('poll response', greeting);
if (greeting.id == x) {
karate.log('condition satisfied, exiting');
return;
}
karate.log('sleeping');
// uncomment / modify the sleep time as per your wish
// java.lang.Thread.sleep(1000);
}
}
EDIT - also see: https://stackoverflow.com/a/55823180/143475
The follow code can correctly run now:
Feature:
Background:
* url 'url'
Scenario:
* def input =
"""
{
'form': {},
'query': {},
}
"""
* path '/rest/n/test'
* params input.query
* form fields input.form
* method post
* status 200
* math response contains { result: 1 }
* eval if (response.result != 1) karate.call('delete-user.feature'))
So, I hope hope retryPost method which can retry-post the scenario (it can auto check status).
or:
...
* eval if (responseStatus == 5xx) retryPost/retryGet/retryPut
* eval if (response.result != 1) retryPost/retryGet/retryPut
Here retryPost/retryGet/retryPut only re-run the section code.
for example:
Feature:
Background:
* url 'url'
Scenario:
# section 1
...
* method post
* eval if () retryPost # only re-run section 1
# section 2
...
* method post
*eval if () retryPost # only re-run section 2
Thanks a lot!

Is there a simple way to identify all dependent records of a record using SuiteScript in NetSuite?

In deleting existing records during automated tests, I often encounter the "This record cannot be deleted because it has dependent records" error. Right now I scrape the dependent records page for the record types and ID's of each dependent record and delete them with nlapiDeleteRecord before proceeding. There must be a better way to identify and delete dependent records.
If the record that is returning that error is a Transaction you could use a few different ways to identify them.
You could look through the "Links" line items on the NetSuite record object like so: (note that it does return some weird record type strings ("Bill" when it should be "vendorbill", etc):
var recordtype = 'purchaseorder'; // Any valid transaction record type
var recordid = 2709498; // Any valid Transaction Internal ID that has links
var recordlinks = getRecordLinks(recordtype,recordid);
/**
* Returns the an array containing the Record Type and Internal Id of each record linked to the input record
* #param {Object} rec - A NetSuite record object, or record type internal id (salesorder, purchaseorder, etc)
* #param {String} recordid - The Internal ID of the record to load (if a nlobjRecord was not provided)
* #return {Array} - Returns an array of links, or null
*/
function getRecordLinks(rec,recordid) {
if(!(typeof rec == 'object')) rec = nlapiLoadRecord(rec,recordid);
if(rec.getLineItemCount('links') == 0) return null;
var links = [];
for(var i=1,total=rec.getLineItemCount('links');i<=total;i++) {
links.push({type:rec.getLineItemValue('links','type',i),id:rec.getLineItemValue('links','id',i)});
}
return links;
}
Or, you could just execute a search to find them. Probably faster, and returns the correct record type for each link:
var recordid = 2709498; // Any valid Transaction Internal ID that has links
var links = nlapiSearchRecord('transaction',null,[['mainline','IS','T'],'AND',['createdfrom','IS',recordid]]);
if(links != null) {
for(var i=0,total=links.length;i<total;i++) {
var link_recordtype = links[i].getRecordType();
var link_recordid = links[i].getId();
console.log('Link type:'+link_recordtype+' ID:'+link_recordid);
// nlapiDeleteRecord(link_recordtype,link_recordid);
}
}

Resources