Passing javascript string variable into nlapiSearchRecord filter - suitescript

I'm working on a Suitelet to apply landed costs (through a .csv file) to item receipts. This code below is iterating through an array of PO numbers to build out a formula(number) to pass into nlapiSearchRecord().
I'm having trouble getting the search to fire when passing part of the search filter in from a variable. I've tried passing various sizes of string with and without double or single quotes (see commented out sections for an idea) and now I'm simply passing one variable, testNumber, into the search string. It's still failing giving me this error:
SSS_INVALID_SRCH_FILTER_EXPR_OBJ_TYPEMalformed search filter expression: Unrecognized object type.
The complete line is supposed to be
["formulanumeric: case when {number} = 'PO476' or {number} = '294' then 1 else 2 end","equalto","1"],
for(var i = 0; i<poNumbers.length; i++) {
if(i < (poNumbers.length - 1)) {
poFormula += "{number} = '"+poNumbers[i]+"' or ";
}
else {
poFormula += "{number} = '"+poNumbers[i]+"'";
}
}
//poFormula(string) --> {number} = 'PO481' or {number} = 'PO476' or {number} = '294' or {number} = 'PO440' or {number} = 'PO441'
//var searchFormulaStart = "formulanumeric: case when "+poFormula+" then 1 else 2 end";
//var sfMiddle = "equalto";
//var sfEnd = "1";
var testNumber = "'PO476'";
var purchaseorderSearch = nlapiSearchRecord("purchaseorder",null,
[
["mainline","is","T"],
"AND",
["type","anyof","PurchOrd"],
"AND",
["formulanumeric: case when {number} = "+testNumber+" then 1 else 2 end","equalto","1"],
//[searchFormulaStart,sfMiddle,sfEnd],
"AND",
["type","anyof","PurchOrd"]
],
[
new nlobjSearchColumn("internalid",null,null)
]
);
The idea is that I need to return all the POs included in the .csv so I can get their internal ids. Later in the code, I will pass these ids into another search against item receipts, finding all receipts created from those POs. Once I have those, I can apply freight costs to those receipts.
var itemreceiptSearch = nlapiSearchRecord("itemreceipt",null,
[
["type","anyof","ItemRcpt"],
"AND",
["mainline","is","T"],
"AND",
["createdfrom","anyof", poInternalIds]
],
[
new nlobjSearchColumn("tranid",null,null)
]
);
Can anyone confirm if there's some undocumented bug or something with passing part of a search string in from a variable like this? Alternatively, given PO numbers and freight costs, is there a better way to apply landed costs to item receipts?
Thanks!

I found a solution with help from some folks on Slack. Rather than trying to concatenate variables into the filter string, I'm now creating the entire filter array in a forEach() loop as shown below.
I'm still not sure why the Netsuite API didn't like my search formatted with a concatenation, but this is a much cleaner solution anyway.
for(var i = 0; i<freightCosts.length; i++) {
poNumbers.push(freightCosts[i].PO);
}
poNumbers.forEach(function(tranid){
filters.push(["tranid", "is", tranid]);
filters.push("or");
});
// remove the last "or"
filters.pop();
var purchaseorderSearch = nlapiSearchRecord("purchaseorder",null,
[
["mainline","is","T"],
"AND",
["type","anyof","PurchOrd"],
"AND",
[filters]
],
[
new nlobjSearchColumn("internalid",null,null)
]
);

Weirdly enough, I ran into this exact issue just last week. It appears to be a bug in the Rhino Javascript engine that Netsuite uses. The workaround to get it to work is to wrap your concatenation in a String():
[String("formulanumeric: case when {number} = "+ testNumber + " then 1 else 2 end"), "equalto", "1"]

Related

Filtering an array....of hotels

let thisHotel: number = this.$("#hotelsList").val(); // the selected hotel Id value for the one I want..
let hotels2 = this.getModel().get("hotels"); // all the hotels
I have the selected hotel I want in my massive array...
I can see the hotels in the debug by doing specifically
console.log(hotels2.hotels);
thisHotel currently = 4 which is the value of one of the hotels.
Loop through hotels2 then the object is under hotels then the specific Id is 4.
I need to get the entire object for the specific hotel where Id = 4
I know there are a number of ways to do this. Some people may use Javascript and some people may use Jquery. What would you do?
Every example I see is always referring to a simple array with one or two values.
This is a more complex object.. I guess these are just the way it works in real life examples.
I'm a newbie so please help me get my bearings ...
I did it this way... if you have better suggestions let me know please...
let foundHotel;
//console.log("Hotel Count=" + hotels2.hotels.length);
for (var i = 0; i < hotels2.hotels.length; i++) {
var x = hotels2.hotels[i];
console.log("Looping thru " + x.Id + " checking for " + thisHotel);
if (x.Id == thisHotel) {
console.log("Match on " + x.HotelInformation.Name);
foundHotel = hotels2.hotels[i];
break;
}
}
console.log(foundHotel);
Then I use jquery to prepopulate my form with the info about the selected hotel....
$("#HotelName").val(foundHotel.HotelInformation.Name);
$("#HotelChainCode").val(foundHotel.HotelInformation.ChainCode);

How to separate multiple columns from a range in an array?

I have a range of data in a Google Sheet and I want to store that data into an array using the app script. At the moment I can bring in the data easily enough and put it into an array with this code:
var sheetData = sheet.getSheetByName('Fruit').getRange('A1:C2').getValues()
However, this puts each row into an array. For example, [[Apple,Red,Round],[Banana,Yellow,Long]].
How can I arrange the array by columns so it would look: [[Apple,Banana],[Red,Yellow],[Round,Long]].
Thanks.
It looks like you have to transpose the array. You can create a function
function transpose(data) {
return (data[0] || []).map (function (col , colIndex) {
return data.map (function (row) {
return row[colIndex];
});
});
}
and then pass the values obtained by .getValues() to that function..
var sheetData = transpose(sheet.getSheetByName('Fruit').getRange('A1:C2').getValues())
and check the log. See if that works for you?
Use the Google Sheets API, which allows you to specify the primary dimension of the response. To do so, first you must enable the API and the advanced service
To acquire values most efficiently, use the spreadsheets.values endpoints, either get or batchGet as appropriate. You are able to supply optional arguments to both calls, and one of which controls the orientation of the response:
const wb = SpreadsheetApp.getActive();
const valService = Sheets.Spreadsheets.Values;
const asColumn2D = { majorDimension: SpreadsheetApp.Dimension.COLUMNS };
const asRow2D = { majorDimension: SpreadsheetApp.Dimension.ROWS }; // this is the default
var sheet = wb.getSheetByName("some name");
var rgPrefix = "'" + sheet.getName() + "'!";
// spreadsheetId, range string, {optional arguments}
var single = valService.get(wb.getId(), rgPrefix + "A1:C30");
var singleAsCols = valService.get(wb.getId(), rgPrefix + "A1:C30", asColumn2D);
// spreadsheetId, {other arguments}
var batchAsCols = valService.batchGet(wb.getId(), {
ranges: [
rgPrefix + "A1:C30",
rgPrefix + "J8",
...
],
majorDimension: SpreadsheetApp.Dimension.COLUMNS
});
console.log({rowResp: single, colResp: singleAsCols, batchResponse: batchAsCols});
The reply will either be a ValueRange (using get) or an object wrapping several ValueRanges (if using batchGet). You can access the data (if any was present) at the ValueRange's values property. Note that trailing blanks are omitted.
You can find more information in the Sheets API documentation, and other relevant Stack Overflow questions such as this one.

coercing a paramter into an integer in Body Mapping Template, AWS API Gateway

I've been using a bit of arithmetic in the Body Mapping Template in Integration Request:
#set($now = $context.requestTimeEpoch/1000)
#set($soon = $now + 600)
{
"TableName": "events",
.... [ here get events between $now and $soon]
}
Recently I came to need to pass an offset through a parameter:
#set($now = $context.requestTimeEpoch/1000)
#set($soon = $now + $input.params('offset'))
{
"TableName": "events",
.... [ here get events between $now and $soon] ....
}
It turns out that if $now is 1518939082, with query paramter ?offset=600 $soon will be 1518939082600 - a concatenation. I have tried various ways to force the parameter being recognised as an integer, including:
#set($offset = $Integer.parseInt($input.params('offset')))
#set($offset = 0 + $input.params('offset'))
#set($offset = 1 * $input.params('offset'))
None of them works. I inserted #set($offset = 0) before each test so I can tell "nothing happens" from "a nothingness is returned".
In the first case, $offset prints an empty string, not 0. (This happens to $Integer.parseInt("1") too.)
In the second case, $offset prints a concatenation of "0" and the string value of "offset".
In the third case, $offset prints a 0, as if the entire line doesn't exist.
None of them successfully transformed the parameter to an integer.
Is there a way to use that parameter as an integer?

Crossfilter: how to build custom reduce functions when I want to access a specific array-value?

I have constructed my crossfilter-setup a bit different than in most examples I can find, namely:
I have data-array d with multiple data-sources included, among which is data1.
var cf = crossfilter(d3.range(0, d.data1.length));
Then I construct my dims like:
var dim = cf.dimension(function(i) { return d.data1[i].id; });
And I construct my groups like:
var group = dim.group().reduceSum(function(i) { return d.data1[i].total;});
This all works fine, but when I want to create custom reduce functions, the extra parameter i is giving me trouble.
var reduceAddPerc = function(p,v) {
p.sumOfSub += d.data1[i].var1;
p.sumOfTotal += d.data1[i].total;
p.finalVal = p.sumOfSub / p.sumOfTotal;
return p;
};
var reduceRemovePerc = function(p,v) {
p.sumOfSub -= d.data1[i].var1;
p.sumOfTotal -= d.data1[i].total;
p.finalVal = p.sumOfSub / p.sumOfTotal;
return p;
};
var reduceInitialPerc = function() {
return {sumOfSub:0, sumOfTotal:0, finalVal:0 };
};
And then defining the group with:
var group = dim.group().reduce(reduceAddPerc,reduceRemovePerc,reduceInitialPerc);
This doesn't work obviously, since the parameter i is now not known within the function. But I've tried adding the parameter (p,v,i), or nesting the functions by creating an additional function with parameter i around the (p,v) function, and also creating an additionao function(i) within the (p,v) function, but I cannot get this to work.
Does anyone have any help to offer?
In the custom reduce functions, the v parameter is the record currently being "reduced". In this case, it should be your counter, so just use it where you would normally use i. Is that not working?

METEOR - Automatically increment order numbers

What I need to do is use either collection-2 or another package to automatically create a new order number, incremented from the last order number used.
i.e. Starting off with PO123456, when I save this order, the next time I make a new PO, it automatically generates the number PO123457.
I've been looking for a good example or tutorial, but I'm not able to find one.
Using konecty:mongo-counter in conjuntion with aldeed:collection2 and aldeed:simple-schema should be pretty straightforward. In your schema definition try:
POnumber: { type: String, autoValue: function(){
if ( this.isInsert ){ // restrict to when inserting a document
var currentNumber = incrementCounter('purchase order'); // this will use mongo-counter
// WARNING: you can only ever get as rich as 10M POs!!
var zeroPad = "000000" + currentNumber; // pad with 6 zeros
zeroPad = zeroPad.substr(zeroPad.length-7); // restrict to 7 places
return 'PO' + zeroPad; // prefix with 'PO'
} else if ( this.isSet ){
this.unset(); // prevent attempts to change the number
}
}

Resources