Reputation: 43
I'm using the Netsuite PHP Toolkit to try to obtain a list of invoices for a customer. I can do the call (using a TransactionSearch) with no problem, but I'm struggling to understand how I'm supposed to get all details for an invoice - i.e. the invoice "header" details (e.g. grand total, currency, main menu line etc) as well as details for each line item (net value, taxable value, item etc).
I have tried a couple of approaches:
TransactionSearchAdvanced, with return columns specified and returnSearchColumns preference set to "false". This gives back all the separate lines (woo!) but things like currency and term aren't expanded out - you just get internalId specified and not the actual text (or the symbol). Also, with TSA, do you really have to specify every column you want? i.e. is the default really just an empty set of fields? Isn't there a way of just saying "give me all the details for all lines of each invoice?
TransactionSearch, with returnSearchColumns preference set to "true". This gives a list of single Invoice type records, with all the currency and term stuff correctly populated, but frustratingly, none of the individual line items. It's more of a summary.
So I am left with a couple of options, neither of which are very palatable, namely:
or
I have no idea how you're supposed to do this, and can't find anything on the internet about it. This is one of the worst interfaces I've used (and I've used some pretty bad ones).
Any help would be hugely appreciated.
Upvotes: 4
Views: 5614
Reputation: 4438
Here is what I have for getting a customer's invoices:
public function getCustomerInvoices($customer_id)
{
$service = new NetSuiteService($this->config);
$customerSearchBasic = new CustomerSearchBasic();
$searchValue = new RecordRef();
$searchValue->type = 'customer';
$searchValue->internalId = $customer_id;
$searchMultiSelectField = new SearchMultiSelectField();
setFields($searchMultiSelectField, array('operator' => 'anyOf', 'searchValue' => $searchValue));
$customerSearchBasic->internalId = $searchMultiSelectField;
$transactionSearchBasic = new TransactionSearchBasic();
$searchMultiSelectEnumField = new SearchEnumMultiSelectField();
setFields($searchMultiSelectEnumField, array('operator' => 'anyOf', 'searchValue' => "_invoice"));
$transactionSearchBasic->type = $searchMultiSelectEnumField;
$transactionSearch = new TransactionSearch();
$transactionSearch->basic = $transactionSearchBasic;
$transactionSearch->customerJoin = $customerSearchBasic;
$request = new SearchRequest();
$request->searchRecord = $transactionSearch;
$searchResponse = $service->search($request);
return $searchResponse->searchResult->recordList;
}
Upvotes: 0
Reputation: 1
Create a new suitelet script and deploy
Below script will give you invoice list by customer internal id
function customSearch(request, response) {
var rows = [];
var result;
var filters = [];
//9989 is customer internal id you can add more
// by pushing additional ids to array
filters.push(new nlobjSearchFilter('entity', null, 'anyOf', [9989] ));
var invoiceList = nlapiSearchRecord('invoice', null, filters, []);
// by default record limit is 1000
// taking 100 records
for (var i = 0; i < Math.min(100, invoiceList.length); i++)
{
if (parseInt(invoiceList[i].getId()) > 0) {
recordid = invoiceList[i].getId();
try {
result= nlapiLoadRecord(invoiceList[i].getRecordType(), recordid);
// pushing in to result
rows.push(result);
} catch (e) {
if (e instanceof nlobjError) {
nlapiLogExecution('DEBUG', 'system error', e.getCode() + '\n' + e.getDetails());
} else {
nlapiLogExecution('DEBUG', 'unexpected error', e.toString());
}
}
}
}
response.setContentType('JSON');
response.write(JSON.stringify({'records' : rows}));
return;
}
}
}
response.setContentType('JSON');
response.write(JSON.stringify({'records' : rows}));
return;
}
Upvotes: 0
Reputation: 276
Just like you I started out trying to do things with the Web Services API (aka SuiteTalk). Mostly it was an exercise in frustration because eventually what I found out was that I plain couldn't do what I wanted with them. That and the performance was pretty bad, which would have killed my project even if it had worked properly.
Like Faz, I've found it much easier and faster to use a combination of RESTlets and Saved Searches than deal with the web services framework.
Basically break your problem down into these parts:
Part I: So the saved search is pretty straightforward. I'm going to assume you can make that happen and also that you can actually get all the fields you want in one place. That hasn't always been the case in my experience.
Part II: The RESTlet involves a lot more steps even though it's really a very simple thing. What makes it complicated is getting it uploaded and deployed on your NetSuite site. If you don't already have the NetSuite IDE installed I highly recommend it if only to make deploying the scripts a little easier. The autocompletion and tooltips are extremely useful as well.
For instance here is code I use to get results from a search I cared about. This was adapted from some kind soul's posting somewhere on the internet but I forget where:
function getSearchResults(){
var max_rows = 1000;
var search_id = 1211;
var search = nlapiLoadSearch(null, search_id);
var results = search.runSearch();
var rows = [];
// add starting point for usage
var context = nlapiGetContext();
startingUsage = context.getRemainingUsage();
rows.push(["beginning usage", startingUsage]);
// now create the collection of result rows in 1000 row chunks
var index = 0;
do{
var chunk = results.getResults(index, index+1000);
if( ! chunk ) break;
chunk.forEach( function(row){
rows.push(row);
index++;
});
}while( chunk.length === max_rows);
// add a line that returns the remaining usage for this RESTlet
context = nlapiGetContext();
var remainingUsage = context.getRemainingUsage();
rows.push(["remaining usage",remainingUsage]);
// send back the rows
return rows;
}
This is where you get things primed by passing in your Saved Search Internal ID:
var search = nlapiLoadSearch(null, SEARCH_ID);
var resultSet = search.runSearch();
Then the code repeatedly calls getResults() to get chunks of 1000 results, this is a NetSuite limitation. Once you have this written you have to upload the script to NetSuite and configure and deploy it. The most important part is telling it what function to assign to each verb. In this case I assigned GET to execute the getSearchResults. There is a lot of work to do here, and I'm not going to type all of it out because it is worth your time to learn this part. At least enough to get the IDE to do it for you =D. You can read all about it in the "Introduction to RESTlets" guide.
Part III. Client code can be in whatever you want that does REST the way you like to. Personally I like Python for this because the requests library is fantastic. Here's some example Python code:
import requests
import json
url = 'https://rest.sandbox.netsuite.com/app/site/hosting/restlet.nl?script=123&deploy=1'
headers = {'Content-Type': 'application/json', 'Authorization':'NLAuth nlauth_account=1234567, [email protected], nlauth_signature=somepassword, nlauth_role=3'}
resp = requests.get(url, headers=headers)
data = resp.json()
The URL is going to be displayed to you as part of the deployment of the RESTlet. Then it's up to you to do what you want with the data that comes back.
So the things I would suggest you spend time with would be
I hope that helps.
Upvotes: 3
Reputation: 160
I created a saved search in Netsuite and call that search using restlet. With this it is pretty lightweight and you can call the data as it is in the saved search.
Performance wise Restlet is much better than webservices.
Upvotes: 1