Friends'm working with NetSuite and SuiteScript. I can save a purchase order running the script and also charge Purchase Orders created, but when I bring returns data item value as a null value, and I need to get the id of the item.
The result gives me the log NetSuite is:
Purchase Order ID: 3706 Vendor ID: 144 Item ID: null Trandate: 06/08/2015 Form: Standard Purchase Order Currency: Peso CL
this happens all Purchase Orders and obviously if you have an item attached.
function to load javascript to use Purchase Order is as follows:
function loadPurchaseOrder(){
nlapiLogExecution('DEBUG','loadPurchaseOrder', 'Entra a funcion loadPurchaseOrder');
//se aplican filtros para la busqueda del objeto
var filters= new Array();
filters[0] = new nlobjSearchFilter('purchaseorder',null,'isnotempty');
filters[1] = new nlobjSearchFilter('mainline', null, 'is', 'T');
//seleccion de los campos que se quieren extraer
var columns = new Array();
columns[0] = new nlobjSearchColumn('item');
columns[1] = new nlobjSearchColumn('entity');
columns[2] = new nlobjSearchColumn('trandate');
columns[3] = new nlobjSearchColumn('customform');
columns[4] = new nlobjSearchColumn('currency');
columns[5] = new nlobjSearchColumn('internalid');
var results = nlapiSearchRecord('purchaseorder',null,filters,columns);
var out = "";
if(results != null ){
for(var i=0; i< results.length; i++){
var purchaseOrder = results[i];
var idItem = purchaseOrder.getValue('item');
var idVendor = purchaseOrder.getValue('entity');
var trandate = purchaseOrder.getValue('trandate');
var form = purchaseOrder.getText('customform');
var currency = purchaseOrder.getText('currency');
var idPurchaseOrder = purchaseOrder.getText('internalid');
out = " ID Purchase Order: " + idPurchaseOrder + " ID Vendor: " + idVendor + " ID Item: " + idItem
+ " Trandate: " + trandate + " Form: " + form + " Currency: " + currency;
nlapiLogExecution('DEBUG','purchaseOrderCargada', out);
}
}
return out;
}
If someone could please help me. Greetings!
pd:
I've also tried:
var idItem = nlapiGetLineItemField ('item', 'item');
and it does not work = /
This is maybe a longer answer than you're expecting, but here we go.
NetSuite divides Transaction records (Purchase Order is a type of Transaction) into Body and Line Item fields. When you do a Transaction search that includes mainline = 'T', you are telling NetSuite to only retrieve Body field data. The item field, however, is a Line Item field, so NetSuite will not return any data for it. That's why idItem is null.
Understanding the behaviour of the mainline filter is crucial to Transaction searches. Basically, it goes like this:
mainline = 'T' will only return body field data, so it will return exactly one search result per Transaction
mainline = 'F' will only return line item data, so it will return one search result for every line item on matching Transactions
mainline not specified will return both body field and line data, so it will return one result for each transaction itself plus one result for each line on each transaction.
Here's a concrete example. Let's say that there is only one Purchase Order in the system that matches all of your other search filters (besides mainline), and that Purchase Order has three items on it. This is how the search results will change based on the mainline filter:
If mainline = 'T' then you will get exactly one result for the Purchase Order, and you will only get data for Search Columns that are Body fields.
If mainline = 'F' then you will get exactly three results, one for each line item, and all of your Search Columns will contain data whether they are Body or Line fields
If mainline is not specified then you will get exactly four results, one of them will only contain data for Body fields, and the other three will contain both Line and Body data
It's difficult to advise on exactly how you should change your search as I do not know what you plan to do with these search results.
Related
I'm designing a script that takes an object (jsonData[data]) and inputs its values into a different sheet based on which product it is.
Currently the script inputs all the data into a new row each time the form reaches a new stage, however the form goes through 4 stages of approval and so I'm finding each submission being entered into 4 different rows. Each submission has an "Id" value within the object which remains the same (but each submission could also be on any row in the sheet as it's used a lot).
I'm checking whether the ID exists in the sheet and using iteration to find the row number:
function updatePlatformBulkInfo(jsonData) {
var sheetUrl = "https://docs.google.com/spreadsheets/d/13U9r9Lu2Fq1WTT8pQ128heCm6_gMmH1R4O6u8e7kvBo/edit#gid=0";
var sheetName = "PlatformBulkSetup";
var doc = SpreadsheetApp.openByUrl(sheetUrl);
var sheet = doc.getSheetByName(sheetName);
var rowList = [];
var formId = jsonData["Id"];
var allSheetData = sheet.getDataRange().getValues();
setLog("AllSheetData = " + allSheetData[1][11]) //Logs to ensure data is collected correctly
var rowEdited = false;
var rowNumber = 0;
//Check whether ID exists in the sheet
for (var i = 0; i < allSheetData.length; i++) {
if(allSheetData[i][11] == formId) {
rowEdited = true;
} else {
rowNumber += 1;
}
}
My issue is with the next part:
//Append row if ID isn't duplicate or update row if duplicate found
if (rowEdited == false) {
for (var data in jsonData) {
rowList.push(jsonData[data])
}
setLog("***Row List = " + rowList + " ***");
setLog("***Current Row Number = " + rowNumber + " ***");
sheet.appendRow(rowList);
} else if(rowEdited == true){
var newRowValue = jsonData[data];
sheet.getRange(rowNumber, 1).setValues(newRowValue);
}
Everything works fine if the duplicate isn't found (the objects values are appended to the sheet). But if a duplicate is found I'm getting the error:
Cannot find method setValues(string)
This looks to me like i'm passing a string instead of an object, but as far as I'm aware I've already converted the JSON string into an object:
var jsonString = e.postData.getDataAsString();
var jsonData = JSON.parse(jsonString);
How can I modify my script to write the updated data to the matched row?
It's unclear based on your code whether or not you will actually write to the correct cell in the case of a duplicate. As presented, it looks as though you loop over the sheet data, incrementing a row number if the duplicate is not found. Then, after completing the loop, you write to the sheet, in the row described by rowNumber, even though your code as written changes rowNumber after finding a duplicate.
To address this, your loop needs to exit upon finding a duplicate:
var duplicateRow = null, checkedCol = /* your column to check */;
for(var r = 0, rows = allSheetData.length; r < rows; ++r) {
if(allSheetData[r][checkedCol] === formId) {
// Convert from 0-base Javascript index to 1-base Range index.
duplicateRow = ++r;
// Stop iterating through allSheetData, since we found the row.
break;
}
}
In both cases (append vs modify), you seem to want the same output. Rather than write the code to build the output twice, do it outside the loop. Note that the order of enumeration specified by the for ... in ... pattern is not dependable, so if you need the elements to appear in a certain order in the output, you should explicitly place them in their desired order.
If a duplicate ID situation is supposed to write different data in different cells, then the following two snippets will need to be adapted to suit. The general idea and instructions still apply.
var dataToWrite = [];
/* add items to `dataToWrite`, making an Object[] */
Then, to determine whether to append or modify, test if duplicateRow is null:
if(dataToWrite.length) {
if(duplicateRow === null) {
sheet.appendRow(dataToWrite);
} else {
// Overwriting a row. Select as many columns as we have data to write.
var toEdit = sheet.getRange(duplicateRow, 1, 1, dataToWrite.length);
// Because setValues requires an Object[][], wrap `dataToWrite` in an array.
// This creates a 1 row x N column array. If the range to overwrite was not a
// single row, a different approach would be needed.
toEdit.setValues( [dataToWrite] );
}
}
Below is the most basic solution. At the end of this post, I'll expand on how this can be improved. I don't know how your data is organized, how exactly you generate new unique ids for your records, etc., but let's assume it looks something like this.
Suppose we need to update the existing record with new data. I assume your JSON contains key-value pairs for each field:
var chris = {
id:2,
name: "Chris",
age: 29,
city: "Amsterdam"
};
Updating a record breaks down into several steps:
1) Creating a row array from your object. Note that the setValues() method accepts a 2D array as an argument, while the appendRow() method of the Sheet class accepts a single-dimension array.
2) Finding the matching id in your table if it exists. The 'for' loop is not very well-suited for this idea unless you put 'break' after the matching id value is found. Otherwise, it will loop over the entire array of values, which is redundant. Similarly, there's no need to retrieve the entire data range as the only thing you need is the "id" column.
IMPORTANT: to get the row number, you must increment the array index of the matching value by 1 as array indices start from 0. Also, if your spreadsheet contains 1 or more header rows (mine does), you must also factor in the offset and increment the value by the number of headers.
3) Based on the matching row number, build the range object for that row and update values. If no matching row is found, call appendRow() method of the Sheet class.
function updateRecord(query) {
rowData = [];
var keys = Object.keys(query);
keys.forEach(function(key){
rowData.push(query[key]);
})
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheets()[0];
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var idColumn = 1;
var ids = sheet.getRange(2, idColumn, sheet.getLastRow() - 1, 1).getValues();
var i = 0;
var matchedRow;
do {
if (ids[i] == query.id) { matchedRow = i + 2; }
i++;
} while (!matchedRow && i < ids.length);
if (matchedRow) {
var row = sheet.getRange(matchedRow, idColumn, 1, rowData.length);
row.setValues([rowData]);
} else {
sheet.appendRow(rowData);
}
}
NOTE: if your query contains only some fields that need to be updated (say, the 'id' and the 'name' field), the corresponding columns for these fields will be
headers.indexOf(query[key]) + 1;
Possible improvements
If the goal is to use the spreadsheet as a database and define all CRUD (Create, Read, Write, Delete) operations. While the exact steps are beyond the scope of the answer, here's the gist of it.
1) Deploy and publish the spreadsheet-bound script as a web app, with the access set to "anyone, even anonymous".
function doGet(e) {
handleResponse(e);
}
function doPost(e) {
handleRespone(e);
}
function handleResponse(e) {
if (e.contentLength == -1) {
//handle GET request
} else {
//handle POST request
}
}
2) Define the structure of your queries. For example, getting the list of values and finding a value by id can be done via GET requests and passing parameters in the url. Queries that add, remove, or modify data can be sent as payload via POST request. GAS doesn't support other methods besides GET and POST, but you can simulate this by including relevant methods in the body of your query and then selecting corresponding actions inside handleResponse() function.
3) Make requests to the spreadsheet URL via UrlFetchApp. More details on web apps https://developers.google.com/apps-script/guides/web
I need to add multiple values (ID fields of another custom list) to a Multiple Lookup field using Sp-services.
What is the correct format of the data to be used ?
I have tried like ( 5,9,6 ) but only selecting the first item.
I am not quite sure why do you want to do this, because these items you will add - they can't be saved as values, because there is a relationship between the column and lookup list, i.e. if you have Lookup column to the List1 and you add a new value from the List2 with id 99 and save, it will save the reference to the list item with id 99 in the List1.
but if anything, it is possible, this is how I am appending multiple lookup selected values:
var lines = additionalTechnologies.split(';#');
$.each(lines, function (index) {
if (lines[index].length < 3) {
$("select[title='Additional Technologies selected values']:first").append("<option value=" + lines[index] + " title=" + lines[index + 1] + ">" + lines[index + 1] + "</option>");
$("select[title='Additional Technologies possible values'] option[value=" + lines[index] + "]").remove();
}
});
and remove them from the all items list. just do it vice versa.
I have found a way to do it.
// "list1Id" contains the array of LIST1 ID fields that you want to add...
// "MULTIPLELOOKUPFIELD" is the multiple lookup field in the LIST2...
var multipleLookupValue ="";
for(i = 0; i < list1Id.length ; i++)
{
multipleLookupValue = multipleLookupValue + list1Id[i]+";#data;#";
}
var method = "UpdateListItems";
$().SPServices({
operation: method,
async: false,
batchCmd: "New",
listName: "LIST2" ,
valuepairs: [["MULTIPLELOOKUPFIELD",multipleLookupValue]],
completefunc: function (xData, Status) {
//alert("Added new item to LIST2 list");
}
});
May be it will help someone...
I'm trying to get a field to update with each line item on the invoice (without over writing what is already there), using a Query Expression to get the data that needs to be used to update the field.
So far I've been able to get this to work just fine when only 1 line item is present. But whenever I test this against multiple line items I get the " The given key was not present in the dictionary." error.
Any help or nudge in the right direction?
QueryExpression lineitem = new QueryExpression("invoicedetail");
lineitem.ColumnSet = new ColumnSet("quantity", "productid", "description");
lineitem.Criteria.AddCondition("invoiceid", ConditionOperator.Equal, invoiceid);
EntityCollection results = server.RetrieveMultiple(lineitem);
Invoice.Attributes["aspb_bookmarksandk12educational"] = "Purchases";
Invoice.Attributes["aspb_bookmarksandk12educational"] += "\n";
Invoice.Attributes["aspb_bookmarksandk12educational"] += "Product" + " " + "Quantity";
Invoice.Attributes["aspb_bookmarksandk12educational"] += "\n";
foreach (var a in results.Entities)
{
string name = a.Attributes["description"].ToString();
string quantity = a.Attributes["quantity"].ToString();
Invoice.Attributes["aspb_bookmarksandk12educational"] += " " + name + " ";
Invoice.Attributes["aspb_bookmarksandk12educational"] += quantity;
Invoice.Attributes["aspb_bookmarksandk12educational"] += "\n";
}
"The given key was not present in the dictionary."
Suggests the problem lies in the way you are trying to access attribute values and not with the multiple entities returned. When you try get an attribute value, try to check if the attribute exists before reading the value like so:
if (a.Attributes.ContainsKey("description"))
{
var name = a.Attributes["description"] as string;
}
Or even better use the SDK extension methods to help do the check and return a default value for you like so:
var name = a.GetAttributeValue<string>("description");
var quantity = a.GetAttributeValue<decimal>("quantity");
Unable to find a matching line for sublist apply with key: [doc,line] and value: [5489377,1].
I'm seeing this error when I try to update an apply list on a NetSuite transaction object. The "doc" is the internal ID of the object, and the line number seems to correspond to a line number on the object.
Why is this happening? Can't seem to find a solution.
This works for applying a credit memo to a particular invoice. invId is the internalid of the invoice record:
function applyPayment(creditMemo, payAmount, invId){
var didApply = false;
creditMemo.setFieldValue('autoapply', 'F');
if(payAmount === null) payAmount = creditMemo.getFieldValue('amountremaining');
for(var i = 1; i<=creditMemo.getLineItemCount('apply'); i++){
if(invId == creditMemo.getLineItemValue('apply', 'doc', i)){
didApply = true;
creditMemo.setLineItemValue('apply', 'apply', i, 'T');
creditMemo.setLineItemValue('apply', 'amount',i, payAmount);
}else if('T' == creditMemo.getLineItemValue('apply', 'apply', i)) creditMemo.setLineItemValue('apply', 'apply', i, 'F');
}
if(didApply) nlapiSubmitRecord(creditMemo);
}
We were getting this error with the Chargebee-Netsuite integration and the solution was to open the corresponding Accounting Period in Netsuite and rerun the sync.
Like you mentioned, the first number[5489377,1] is the Netsuite internal ID of the affected document. If you navigate to the document in Netsuite and it has a padlock this could be the reason Locked document
Open the Accounting Period for the affected document and rerun the sync. setup/accounting/manage accounting periods Manage accounting periods
I am trying to retrieve the entries count for a multiple category view (two categories) using a key.
var db:NotesDatabase = session.getDatabase(sessionScope.serverPath,sessionScope.dbName);
var luview = db.getView(sessionScope.viewName);
var vec:NotesViewEntryCollection = null;
if (key != null) {
vec = luview.getAllEntriesByKey(key);
}
count = vec.getCount().toFixed()
The count being returned is incorrect. I have over 500 documents in the view. It seems to be returning just the document count (20) of the first sub-category.
I've found mention of this as a bug in the forums. I'm running this on a 9.0 server. Any pointers would be much appreciated.
What I would like is the total count - categories (25) + documents (500), that I can use in the repeat control limit.
Thanks,
Dan
I was able to resolve this by using the NotesViewNavigator.
var nav:NotesViewNavigator = v.createViewNavFromCategory(key);
var entry:NotesViewEntry = nav.getFirst();
while (entry != null) {
count = count + 1;
var tmpentry:NotesViewEntry = nav.getNext(entry);
entry.recycle();
entry = tmpentry;
}
Dan - If you can get the entry count into the view column with an #AllChildren... or #AllDecendents or something like that then using the view navigator you should be able to read that value in and not have to actually loop through all the documents.
Another way is to create a different view, could be hidden, and not categorize that second column. Then you're original solution should work I'd think.