trying to extract rowIds from smartsheet using node.js - node.js

I am trying to iterate through and existing smartsheet to get the rowIds (which I need for setting parent/child relationships).
I am struggling :-(
Obviously I don't get the required syntax on this one. Here is what I tried:
var options = {
Id: *******************
};
var row_ids = '';
// get the sheet
smartsheet.sheets.getSheet(options)
.then(function(sheetinfo) {
// iterate through the rows array and build comma-delimited list of row ids
for (rowIds in sheetinfo) {
row_ids += row_ids.concat(', ');
}
});
console.log(row_ids);
Any help would be most appreciated.
Bowow99

In the Get Sheet response, sheetInfo contains a collection of rows and each row object in that collection contains the property id. So to successfully implement the scenario you've described, you need to loop through the sheetInfo.rows array, and for each row in that array, append the value of its id property (followed by a comma) to create the comma-delimited string that you're attempting to build. At the end of this loop, there'll be an extra comma at the end (following the last row id you added to the string) -- so you'll need to remove it using the substring function.
The following code does what I've described above. Please note, I'm not a JavaScript expert -- so there may be a more efficient way to do this...but this code gets you the end result you're after: a comma-delimited string of the row Ids within the specified sheet.
var options = {
id: 8337220210845572 // Id of Sheet
};
var row_ids = '';
// Get sheet
smartsheet.sheets.getSheet(options)
.then(function(sheetInfo) {
// iterate through rows in the sheet and build comma-delimited string of row ids
for (var i=0; i < sheetInfo.rows.length; i++) {
row_ids += sheetInfo.rows[i].id + ',';
}
// remove the final (excess) comma from the end of the row_ids string
row_ids = row_ids.substring(0, row_ids.length-1);
console.log(row_ids);
})
.catch(function(error) {
console.log(error);
});

Related

How do I download data trees to CSV?

How can I export nested tree data as a CSV file when using Tabulator? I tried using the table.download("csv","data.csv") function, however, only the top-level data rows are exported.
It looks like a custom file formatter or another option may be necessary to achieve this. It seems silly to re-write the CSV downloader, so while poking around the csv downloader in the download.js module, it looks like maybe adding a recursive function to the row parser upon finding a "_children" field might work.
I am having difficulty figuring out where to get started.
Ultimately, I need to have the parent-to-child relationship represented in the CSV data with a value in a parent ID field in the child rows (this field can be blank in the top-level parent rows because they have no parent). I think I would need to include an ID and ParentID in the data table to achieve this, and perhaps enforce the validation of that key using some additional functions as data is inserted into the table.
Below is currently how I am exporting nested data tables to CSV. This will insert a new column at the end to include a parent row identifier of your choice. It would be easy to take that out or make it conditional if you do not need it.
// Export CSV file to download
$("#export-csv").click(function(){
table.download(dataTreeCSVfileFormatter, "data.csv",{nested:true, nestedParentTitle:"Parent Name", nestedParentField:"name"});
});
// Modified CSV file formatter for nested data trees
// This is a copy of the CSV formatter in modules/download.js
// with additions to recursively loop through children arrays and add a Parent identifier column
// options: nested:true, nestedParentTitle:"Parent Name", nestedParentField:"name"
var dataTreeCSVfileFormatter = function(columns, data, options, setFileContents, config){
//columns - column definition array for table (with columns in current visible order);
//data - currently displayed table data
//options - the options object passed from the download function
//setFileContents - function to call to pass the formatted data to the downloader
var self = this,
titles = [],
fields = [],
delimiter = options && options.delimiter ? options.delimiter : ",",
nestedParentTitle = options && options.nestedParentTitle ? options.nestedParentTitle : "Parent",
nestedParentField = options && options.nestedParentField ? options.nestedParentField : "id",
fileContents,
output;
//build column headers
function parseSimpleTitles() {
columns.forEach(function (column) {
titles.push('"' + String(column.title).split('"').join('""') + '"');
fields.push(column.field);
});
if(options.nested) {
titles.push('"' + String(nestedParentTitle) + '"');
}
}
function parseColumnGroup(column, level) {
if (column.subGroups) {
column.subGroups.forEach(function (subGroup) {
parseColumnGroup(subGroup, level + 1);
});
} else {
titles.push('"' + String(column.title).split('"').join('""') + '"');
fields.push(column.definition.field);
}
}
if (config.columnGroups) {
console.warn("Download Warning - CSV downloader cannot process column groups");
columns.forEach(function (column) {
parseColumnGroup(column, 0);
});
} else {
parseSimpleTitles();
}
//generate header row
fileContents = [titles.join(delimiter)];
function parseRows(data,parentValue="") {
//generate each row of the table
data.forEach(function (row) {
var rowData = [];
fields.forEach(function (field) {
var value = self.getFieldValue(field, row);
switch (typeof value === "undefined" ? "undefined" : _typeof(value)) {
case "object":
value = JSON.stringify(value);
break;
case "undefined":
case "null":
value = "";
break;
default:
value = value;
}
//escape quotation marks
rowData.push('"' + String(value).split('"').join('""') + '"');
});
if(options.nested) {
rowData.push('"' + String(parentValue).split('"').join('""') + '"');
}
fileContents.push(rowData.join(delimiter));
if(options.nested) {
if(row._children) {
parseRows(row._children, self.getFieldValue(nestedParentField, row));
}
}
});
}
function parseGroup(group) {
if (group.subGroups) {
group.subGroups.forEach(function (subGroup) {
parseGroup(subGroup);
});
} else {
parseRows(group.rows);
}
}
if (config.columnCalcs) {
console.warn("Download Warning - CSV downloader cannot process column calculations");
data = data.data;
}
if (config.rowGroups) {
console.warn("Download Warning - CSV downloader cannot process row groups");
data.forEach(function (group) {
parseGroup(group);
});
} else {
parseRows(data);
}
output = fileContents.join("\n");
if (options.bom) {
output = "\uFEFF" + output;
}
setFileContents(output, "text/csv");
};
as of version 4.2 it is currently not possible to include tree data in downloads, this will be comming in a later release

Deleting rows in excel table with office-js

I have an ajax call in my add in which it should create or update the table in excel. If table is already exists, it should remove the rows and add the new results.
When deleting the rows in loop, it is deleting some rows and then I am getting following error:
Debug info: {"code":"InvalidArgument","message":"The argument is invalid or missing or has an incorrect format.","errorLocation":"TableRowCollection.getItemAt"}
My ajax call in my excel web add-in looks like this:
$.ajax({
//....
}).done(function (data) {
Excel.run(function (ctx) {
var odataTable = ctx.workbook.tables.getItemOrNullObject("odataTable");
//rows items are not available at this point, that is why we need to load them and sync the context
odataTable.rows.load();
return ctx.sync().then(function () {
if (odataTable.rows.items == null) {
odataTable.delete();
odataTable = ctx.workbook.tables.add('B2:G2', true);
odataTable.name = "odataTable";
} else {
console.log("Rows items:" + odataTable.rows.items.length);
odataTable.rows.items.forEach(function (item) {
console.log("Removing row item: " + item.values);
item.delete();
});
console.log("rows cleaned");
}
}).then(function () {
//add rows to the table
});
}).then(ctx.sync);
}).catch(errorHandler);
}).fail(function (status) {
showNotification('Error', 'Could not communicate with the server. ' + JSON.stringify(status));
}).always(function () {
$('#refresh-button').prop('disabled', false);
});
The idea of the iterable collections is that they consist of different items. Once you remove something from these items in a not appropriate way, the collection stops being a collection. This is because they are implemented as a linked list, in which every unit knows only the next unit. https://en.wikipedia.org/wiki/Linked_list
In your case, you are deleting wtih a for-each loop. After the first deletion, the collection is broken. Thus, you need another approach.
Another approach:
Start looping with a normal for loop. Reversed.
E.g.:
for i = TotalRows to 1 i--
if row(i) something then delete
This has already been answered but I recently solved this issue myself and came here to see if anyone had posted a question about it.
When you delete a row, Excel will reorder the row index for each row: i.e. when you delete row 1, row 2 becomes row 1 and all other rows get shifted down 1 index. Because these deletions are pushed to a batch to be completed, when the second deletion is executed, your second row has become row one, so it actually skips the second row and executes what you think is your third row.
If you start from the last row and work backwards, this reordering doesn't occur and neither does the error.
For completeness sake the above example would become:
return ctx.sync().then(function () {
if (odataTable.rows.items == null) {
odataTable.delete();
odataTable = ctx.workbook.tables.add('B2:G2', true);
odataTable.name = "odataTable";
} else {
console.log("Rows items:" + odataTable.rows.items.length);
for (let i = odataTable.rows.items.length -1; i >= 0; i--) // reverse loop
odataTable.rows.items[i].delete();
}
console.log("rows cleaned");
}

Column Value in search api

I want to get the value from a Item sublist of Sales Order record.
But unable to get it. Though I can get the value of entity fields of the SO record.
Below is the snippet of the code:
var filters = new Array();
filters[0] = new nlobjSearchFilter("mainline",null,"is","T");
var column=new Array();
column[0] = new nlobjSearchColumn("trandate");
column[1] = new nlobjSearchColumn("item");
column[2] = new nlobjSearchColumn("cust_col_1");
var result = nlapiSearchRecord('salesorder', null, filters, column);
for(var i = 0; i<result.length; i++)
{
var col = result[i].getAllColumns();
var date = result[i].getFieldValue("trandate"); //I get this
var item_id = result[i].getLineItemValue("item", "item", i+1); // I don't get this
var cust_col = result[i].getLineItemValue("item", "cust_col_1", i+1); //I don't get this
}
I think I am defining the columns wrong.
This part
var item_id = result[i].getLineItemValue("item", "item", i+1); // I don't get this
var cust_col = result[i].getLineItemValue("item", "cust_col_1", i+1); //I don't get this
is also wrong, you use this syntax if you have loaded the record but for search results you just use
result[i].getValue('cust_col_1")
By specifying the filter new nlobjSearchFilter("mainline",null,"is","T"), you're basically telling the search that you don't want any line item data. This means that you will be unable to read any column data, custom or otherwise.
The mainline filter parameter has basically three options, 'F' means you want the line item details. 'T' means you just want the header data. Leaving this filter out will return one row for the header information and one row for each line item on the transaction.

Save document in a for loop using computeWithForm

I have the following button that can save multiple values in a for loop using computeWithForm
//to keep the code simple, I use hardcode value to save document except values in for loop
var forloopvalues = #DbLookup(#DbName(),"CompetencyCourseViewCourseFirst", "myFieldValue1",2 );//use myFieldValue1 to lookup in the view
var date ="13/03/2017";
for(var i =0;i<forloopvalues.length;i++)
{
var mdoc = database.createDocument();
cdate = session.createDateTime(date);
mdoc.replaceItemValue("Form", "myForm");
mdoc.replaceItemValue("myField1","myFieldValue1")
mdoc.replaceItemValue("myField2", forloopvalues[i]); //suppose there are four values in the forloopvalues
mdoc.replaceItemValue("myField3","myFieldValue3");
mdoc.replaceItemValue("myField4", "myFieldValue4");
mdoc.replaceItemValue("myField5", "myFieldValue5");
if(mdoc.computeWithForm(false,false))
{
mdoc.save(true, true);
getComponent("computedField1").setValue("Record saved");
}
else
{
}
}
When I click the button, it can save the document. In the Lotus Notes Client, I can see there are four documents in the view. All the computed fields in the form also filled. However, I notice myField2 only saves the first value in forloopvalues not all values. So although there are four documents, both of them save the first value only in myField2. Therefore, if the computed field in the form that contains myField2 plus other values, it only show the first value in myField2 plus other values
I review the code, there is forloopvalues[i] within the for loop and I don't understand why it only saves the first value only.
For example, assume forloopvalues are valueA, valueB, valueC, valueD. The myField2 only save valueA four times. If there is a computed field in the form that connect myField2 with other values together, the computed field value show valueA+othervalues four times.
I have another attempt, if I use document.save() + On document save in Run form validation in Data tab
In Lotus Notes Client to open the view and read the saved document. It will not show computed field in the form but the loop value is correct
For example, assume forloopvalues are valueA, valueB, valueC, valueD. The myField2 save valueA, valueB, valueC, valueD for each document. If there is a computed field in the form that connect myField2 with other values together, the computed field value show each forloopvalues+othervalues four times (e.g. valueA+othervalues, valueB+othervalues, valueC+othervalues, valueD+othervalues)
I believe computeWithForm is almost near the result although it save the first value in forloopvalues only. In Help Contents, I search about computeWithForm. It's syntax is
computeWithForm(dodatatypes:boolean, raiseerror:boolean) : boolean
and compare to the code in the application I think the syntax is fine. I double check the for loop and not understand why it only save the first value.
So how can I use computeWithForm to save the loop value. Thanks a lot.
update
I think computeWithForm is almost near the result because it can fill all the computed field in the form, but it saves one value in multiple times depends on the number of forloopvalues.
If forloopvalues has one value only, then it saves one time. It forloopvalues has three values, it save three times, each document (in myField2) contains the value of forloopvalues.
If I use document.save(), it can save documents for each value in forloopvalues individually but it will not fill the computed field in the form.
update
I try the following code to see if the value appears correctly inside the function
var forloopvalues = #DbLookup(#DbName(), "CompetencyCourseViewCourseFirst", "myFieldValue1", 2);
for (var index in forloopvalues) {
if (forloopvalues.hasOwnProperty(index)) {
makeAnewDoc(forloopvalues[index]);
}
}
function makeAnewDoc(field2Value) {
var mdoc = database.createDocument();
cdate = session.createDateTime(date);
mdoc.replaceItemValue("Form", "myForm");
mdoc.replaceItemValue("myField1", "myFieldValue1")
mdoc.replaceItemValue("myField2", field2Value);
if (!viewScope.resultMessage) {
viewScope.resultMessage = [];
}
if (mdoc.computeWithForm(false, false)) {
mdoc.save(true, true);
viewScope.resultMessage.push("Record saved " + forloopvalues[index]);
} else {
viewScope.resultMessage.push("Save failed for " + forloopvalues[index]);
}
// Bleeding memory otherwise!
mdoc.recycle();
}
The result is when I click the button, the screen can show this message (suppose forloopvalues has four values)
Record saved valueA ,Record saved valueB ,Record saved valueC ,Record saved valueD
Due to the screen can show the forloopvalues, I feel strange why it only saves the same value four times.
Your code actually would save the last value. Don't loop through the values and save the same document 4 times. ReplaceItemValue can take an array as input. So you can use your lookup result directly. No loop required. Should be faster too
Did misread your question on tiny mobile screen. You need to change your loop slightly:
var forloopvalues = #DbLookup(#DbName(), "CompetencyCourseViewCourseFirst", "myFieldValue1", 2);
var mdoc; // In SSJS (ES3 there are no block level variables!)
for (var index in forloopvalues) {
if (forloopvalues.hasOwnProperty(index)) {
mdoc = database.createDocument();
cdate = session.createDateTime(date);
mdoc.replaceItemValue("Form", "myForm");
mdoc.replaceItemValue("myField1", "myFieldValue1")
mdoc.replaceItemValue("myField2", forloopvalues[index]);
// Bind your UI field to a scope variable
// don't access them directly
if (!viewScope.resultMessage) {
viewScope.resultMessage = [];
}
if (mdoc.computeWithForm(false, false)) {
mdoc.save(true, true);
viewScope.resultMessage.push("Record saved " + index);
} else {
viewScope.resultMessage.push("Save failed for " + index);
}
// Bleeding memory otherwise!
mdoc.recycle();
}
}
or better: pull the function to save the document out (variables are function level in SSJS, not block level):
var forloopvalues = #DbLookup(#DbName(), "CompetencyCourseViewCourseFirst", "myFieldValue1", 2);
for (var index in forloopvalues) {
if (forloopvalues.hasOwnProperty(index)) {
makeAnewDoc(forloopvalues[index]);
}
}
function makeAnewDoc(field2Value) {
var mdoc = database.createDocument();
cdate = session.createDateTime(date);
mdoc.replaceItemValue("Form", "myForm");
mdoc.replaceItemValue("myField1", "myFieldValue1")
mdoc.replaceItemValue("myField2", field2Value);
if (!viewScope.resultMessage) {
viewScope.resultMessage = [];
}
if (mdoc.computeWithForm(false, false)) {
mdoc.save(true, true);
viewScope.resultMessage.push("Record saved " + index);
} else {
viewScope.resultMessage.push("Save failed for " + index);
}
// Bleeding memory otherwise!
mdoc.recycle();
}
Let us know how it goes.
ComputeWithForm broke at some point in the Domino 9.0.1 fix pack cycle. Not sure which FP broke it. IBM has confirmed this issue and is looking at a fix. The onload, onsave, and both options do not work. SPR #LHEYAKALAH

How to access 'Abbreviation' field of a custom list in NetSuite custom lists

I have a custom list that is used as a matrix option of Inventory item. Its 'Color'. This custom list has an abbreviation column. I am creating a saved search on item and using Color field(join) and trying to access 'Abbreviation' field of color.
Abbreviation on custom list is available on when 'Matrix Option List' is checked.
Can someone please help me achieve this? I tried to do this through script and it seems like we cannot access 'Abbreviation' column through script. I also tried to use script to write a search directly on 'Color' - custom list and get the 'abbreviation' through search columns. It did not work. Is there a way to access 'Abbreviation' from custom lists?
Thanks in Advance
You can access it via suitescript by using the record type "customlist" and the internal id of the list like so:
var rec = nlapiLoadRecord('customlist', 5);
var abbreviation = rec.getLineItemValue('customvalue', 'abbreviation', 1);
nlapiLogExecution('DEBUG', 'abbreviation', abbreviation);
Keep in mind that the third argument of getLineItemValue is the line number, not the internal ID of the item in the list. If you want to find a specifc line item, you may want to use rec.findLineItemValue(group, fldnam, value).
Unfortunately, it doesn't look like this translates to saved searches. The suiteanswer at https://netsuite.custhelp.com/app/answers/detail/a_id/10653 has the following code:
var col = new Array();
col[0] = new nlobjSearchColumn('name');
col[1] = new nlobjSearchColumn('internalid');
var results = nlapiSearchRecord('customlist25', null, null, col);
for ( var i = 0; results != null && i < results.length; i++ )
{
var res = results[i];
var listValue = (res.getValue('name'));
var listID = (res.getValue('internalid'));
nlapiLogExecution('DEBUG', (listValue + ", " + listID));
}
However, whatever part of the application layer translates this into a query doesn't handle the abbreviation field. One thing to keep in mind is that the 'custom list' record is basically a header record, and each individual entry is it's own record that ties to it. You can see some of the underlying structure here, but the takeaway is that you'd need some way to drill-down into the list entries, and the saved search interface doesn't really support it.
I could be wrong, but I don't think there's any way to get it to execute in a saved search as-is. I thought the first part of my answer might help you find a workaround though.
Here is a NetSuite SuiteScript 2.0 search I use to find the internalId for a given abbreviation in a custom list.
/**
* look up the internal id value for an abbreviation in a custom list
* #param string custom_list_name
* #param string abbreviation
* #return int
* */
function lookupNetsuiteCustomListInternalId( custom_list_name, abbreviation ){
var internal_id = -1;
var custom_list_search = search.create({
type: custom_list_name,
columns: [ { name:'internalId' }, { name:'abbreviation' } ]
});
var filters = [];
filters.push(
search.createFilter({
name: 'formulatext',
formula: "{abbreviation}",
operator: search.Operator.IS,
values: abbreviation
})
);
custom_list_search.filters = filters;
var result_set = custom_list_search.run();
var results = result_set.getRange( { start:0, end:1 } );
for( var i in results ){
log.debug( 'found custom list record', results[i] );
internal_id = results[i].getValue( { name:'internalId' } );
}
return internal_id;
}
Currently NetSuite does not allows using join on matrix option field. But as you mentioned, you can use an extra search to get the result, you could first fetch color id from item and then use search.lookupFields as follows
search.lookupFields({ type: MATRIX_COLOR_LIST_ID, id: COLOR_ID, columns: ['abbreviation'] });
Note: Once you have internalid of the color its better to use search.lookupFields rather than creating a new search with search.create.

Resources