NetSuite’s new Restlet API is promising for developing external software applications that interact with NetSuite’s rich data and business functionality. For example, imagine a large professional services organization that needs to track timesheets in the NetSuite Advanced Projects module. Most professionals are highly mobile. Instead of logging into NetSuite to do your timesheet via a web browser, imagine a small iPhone or Android app that allows you to quickly enter data. Restlet is the key to make this happen without making a big investment.
This article illustrates how to connect up and create timesheet entries from a Linux Perl program. This example should help bridge the key concepts to hook up to NetSuite through the new Restlet API. The article assumes you have a good understanding of how to create and deploy scripts in the NetSuite environment. The example consists of three files:
- restlet.js: NetSuite program to receive an insert time entry request. It validates data and produces information in the execution log.
- timebill.pl: Perl program that reads timesheet entry data from timebill.csv file. This file has hard coded NetSuite credential information.
- timebill.csv: tab delimited file in the following format: Date <tab> Client: Project <tab> Task <tab> Time in Decimal format <tab> Memo <newline – for new record>
Step 1: Deploy restlet.js
After enabling the NetSuite Restlet API create a new NetSuite Script as type Restlet. Set it up with the following parameters:
- Post Function: “CreateTimebills”
- Deploy the function with a Log Level of “Debug”
- Get the External URL. In our case, it was “https://rest.netsuite.com/app/site/hosting/restlet.nl?script=73&deploy=1 “
Here is the restlet.js code:
function CreateTimebills(datain) { var output = ''; nlapiLogExecution('DEBUG','createRecord',(typeof datain.timebill)); var msg = validateTimeBills(datain); if (msg) { var err = new Object(); err.status = "failed"; err.message = msg; return err; } var timebills = datain.timebill; for (var timebillobject in timebills) { var timebill = timebills[timebillobject]; var trandate = timebill.trandate; var customer = timebill.customer; var casetaskevent = timebill.casetaskevent; var hours = timebill.hours; var memo = timebill.memo; var timebill = nlapiCreateRecord('timebill'); timebill.setFieldValue('trandate', trandate); timebill.setFieldText('customer', customer); timebill.setFieldText('casetaskevent', casetaskevent); timebill.setFieldValue('memo', memo); timebill.setFieldValue('hours', hours); var timebillid = nlapiSubmitRecord(timebill); nlapiLogExecution('DEBUG', 'Timebill ' + timebillid + ' successfully created', timebillid); } return; } function validateTimeBills(datain) { var timebills = datain.timebill; var returnMessage = ""; for (var timebillobject in timebills) { var timebill = timebills[timebillobject]; var trandate = timebill.trandate; var customer = timebill.customer; var casetaskevent = timebill.casetaskevent; var hours = timebill.hours; var memo = timebill.memo; if (isNaN(nlapiStringToDate(trandate))) { returnMessage += "Invalid date: '" + trandate + "'\n"; } if (customer == '') { returnMessage += "Customer entry cannot be blank.'\n"; } if (casetaskevent == '') { returnMessage += "Case Task Event entry cannot be blank.'\n"; } if (hours == '') { returnMessage += "Hours cannot be blank.'\n"; } if (memo == '') { returnMessage += "Memo cannot be blank.'\n"; } } if (returnMessage) { nlapiLogExecution('DEBUG', 'Validation Error', returnMessage); return returnMessage; } }
Step 2: Edit timebill.pl
Edit timebill.pl with your specific NetSuite account ID, username, password, and role. Here is the code:
#!/usr/bin/perl use strict; use warnings; use LWP::UserAgent; use HTTP::Request; use HTTP::Headers; my $inputfilelocation = "timebill.csv"; my $scriptdeployment = "https://rest.netsuite.com/app/site/hosting/restlet.nl?script=73&deploy=1"; my $account = "TSTDRV365788"; my $email = "developer\@prolecto.com"; my $password = ""; my $role = "15"; my $jsonString = & getJSONStringFromFile($inputfilelocation); my $result = & submitRestlet($account, $email, $password, $role, "POST", $scriptdeployment, $jsonString); print $result; exit; sub submitRestlet() { (my $account, my $email, my $password, my $role, my $method, my $deploymentURL, my $request) = @_;# define the HTTP header my $objHeader = HTTP::Headers - > new; $objHeader - > push_header('Authorization' => "NLAuth nlauth_account=$account, nlauth_email=$email, nlauth_signature=$password, nlauth_role=$role"); $objHeader - > push_header('Content-Type' => 'application/json');# make the call my $objRequest = HTTP::Request - > new( $method, $deploymentURL, $objHeader, $request ); my $content = "";# deal with the response my $objUserAgent = LWP::UserAgent - > new; my $objResponse = $objUserAgent - > request($objRequest); if (!$objResponse - > is_error) { return $objResponse - > content; } else { return $objResponse - > error_as_HTML; } } sub getJSONStringFromFile() { (my $inputfilelocation) = @_; open(FILE, $inputfilelocation); my $output = ""; $output = qq { "timebill": [ }; while ( < FILE > ) { chomp; (my $trandate, my $customer, my $casetaskevent, my $hours, my $memo) = split("\t"); $output. = "{"; $output. = qq { "trandate": "$trandate", "customer": "$customer", "casetaskevent": "$casetaskevent", "hours": "$hours", "memo": "$memo" }; $output. = "},"; } close(FILE); chop($output); $output. = qq {] }; $output = "{$output}"; return $output; }
Step 3: Create Timesheet Entry Data
In timebill.csv, create some data as input to insert as NetSuite timesheet entries. Here are a couple sample entries. Note the clients: projects, and tasks must match what is in already in NetSuite to work:
12/05/2011 SmartTech : Test Project 005 Test Task 005 (Task) .75 Meeting on Specifications
12/05/2011 SmartTech : Test Project 005 Test Task 005 (Task) 1 Meeting with Management
The timebill.csv file should be placed in the same local directory as timebill.pl.
Step 4: Execute timebill.pl
Now you can run timebill.pl to see the program work. If all goes right, it will take a few moments and return a code. Go to the NetSuite script deployment log and see if the timesheet entries were created. If so, check the timesheet system to find the new entries.
This article should help you kickstart your restlet system into a working prototype. Stay tuned as we will be writing another article on how we are using restlets to create an Android / iOS app.
This is Awesome! I’ve been looking for examples on Restlets.
Thank you!
Perl code…I think I’m in love.
Seriously though. Great example code. Thank you!
Yes, we perform Perl legacy application development for some long-term clients. The great thing is that it all works and illustrates the model.
Marty
Indeed. Perl is wonderful. Just rarely see it any more, especially in blog posts. I rather miss working with it.
On a semi-related note, have you created file cabinet hosted applications that interface with RESTlets? For example, a static HTML page which utilizes jQuery or Javascript to format the POST data and sends it off? I’m debating between an externally vs. internally hosted page for some sales order interaction.
No, we haven not worked that use case. Yet, this kinds of questions come up when we think about optimizing the license model. Sometimes, we have to go with SuiteLets to produce the equivalent of ResetLets due to limitations with the security model.
Marty
1)How to store and retrieve data(ArrayList of JavaObject) in/from NetSuite from Java web application?
2)If possible, how to create a new NetSuite database table and to store and retrieve data?
P.S: timothy@ghhcommerce.com is our customer login email address
Hello,
First, I believe you are going to want to format your data as JSON between your Java Application and NetSuite.
Second, you can easily create tables in NetSuite via Setup, Customization, Record Types. Then, you can use the Restlet capacities to expose this data. It’s pretty well documented in the NetSuite help guide.
Marty
Hi,
Can you please give me example of how to create restlet using get method
and how to call the same restlet.
Avinash,
Have a look at this article. The code pattern is very similar as it handles both GETS and POSTS. T
https://blog.prolecto.com/2013/08/04/how-to-code-pattern-for-netsuite-driven-jsonp-cross-domain-javascript/
Marty
Hi, I’m trying to write a RESTlet that will create a new employee. So far I have:
var employeeRec = nlapiCreateRecord(’employee’);
employeeRec.setFieldValue(‘lastname’, ‘Test’);
employeeRec.setFieldValue(‘firstname’, ‘Frank’);
employeeRec.setFieldValue(’email’, ‘test9475@frank.org’);
employeeRec.setFieldValue(‘subsidiary’, 3);
var id = nlapiSubmitRecord(employeeRec);
nlapiLogExecution(‘DEBUG’,’bitium: ‘ + id + ‘ employee’,id);
and it works great! But when I try and add:
employeeRec.setFieldValue(‘giveAccess’, true);
employeeRec.setFieldValue(‘password’, ‘somePassword1!’);
employeeRec.setFieldValue(‘password2’, ‘somePassword1!’);
it doesn’t have any effect. How do I make the employee have access to login and set their password after I create the employee? From netsuite web interface I select the “Access” tab.
Hi Andrew,
I suspect what is missing is the related role. Here is a code snippet from some recent work that allows the password to be set:
//snippet
NS_record.setFieldValue('accessrole', role_id);
NS_record.setFieldValue('pass'+'word', password);
NS_record.setFieldValue('pass'+'word2', password);
NS_record.setFieldValue('giveaccess', 'T');
NS_record.setFieldValue('sendemail', 'F')
// watch the optional subsequent parameters
new_id = nlapiSubmitRecord(NS_record, false, true)
Marty
Hi,
Im trying to submit the record inside PUT method in restlet, but it gives me an error?
Here is the code:
function putRESTlet(dataIn) {
nlapiSubmitField(‘invoice’, dataIn.id, ‘custbody202’, ‘T’, true);
}
what seems to be the problem?
Another question, does Restlet issue a submit behind the scene causing my code to fail?
Note: my get request in success. It’s just this put request is giving me hard time.
Thank you in advance.
I recommend you first ensure that the data that is being posted is what you expect. Have you decomposed and inspected the ‘datain’ parameter to make sure that it indeed as the data you expect? Is there an ‘id’ property?
On your second question, no, there is implicate submit behind the scenes with the Restlet. Think of a Restlet as a SuiteLet with an HTTP vocabulary (GET, POST, PUT, DELETE). All that is happening is that the data is being passed to you but you must perform all the logic to make it interact with the NetSuite platform.
Hi,
How to write the Restlet POST Function in NetSuite to set the lineitem values for records like sales order and customer and what would be the JSON request for those ?
Thanks.
Hello Sandy,
The key to work with this is to understand that the Restlet is simply a JSON way to pass information back. That JSON can look like anything you want. Hence, you will need to define a JSON structure for expressing line item values. You could load a Sales Order record with nlapiLoadRecord and then JSON.stringify that object to have one interpretation. But you are free to invent.
Once you get your values for your Sales Order and its respective lines, you will need to parse it and then effectively use the SubLists API to manipulate the line values. Calls to nlapiGetCurrentLineItemValues and nlapiSetCurrentLineItemValue are used. These calls are fundamental SuiteScirpt calls and have no relationship to the RestLet itself. The NetSuite Help document has many examples.
Marty
Hi Marty,
I found your blog very helpful.
Thanks for sharing your knowledge with us.
Having query:
Scenario is i am using scheduled script to call a url.
This url is used to fetch currency details.
But problem is Netsuie automatically converts HTML entities into punctuations.
Please refer code below…
Problem scenario is as :
var date1 = new Date();
nlapiLogExecution(‘DEBUG’, ‘Daily Exchange rate’,’date1 is ‘+date1 );
date1 = nlapiDateToString(date1);
nlapiLogExecution(‘DEBUG’, ‘Daily Exchange rate’,’after nlapiDateToString date1 is ‘+date1 );
var url = “https://www.testname.com/rates/api/v1/rates/USD.csv?”
// ************* we required url in following format
// https://www.testname.com/rates/api/v1/rates/USD.csv?quote=EUR"e=USD"e=CAN"e=IND&date=2015/02/20
var toCurrency = ”;
// ******* arr_toCurrencies contains values like EUR,CAN,IND,USD. *************************
for( var i=0; i < arr_toCurrencies.length ; i++)
{
toCurrency = arr_toCurrencies[i];
nlapiLogExecution('DEBUG', 'Daily Exchange rate','toCurrency is '+toCurrency );
if(i == 0)
{
url +="quote="+toCurrency;
}
else
{
url += ""e="+toCurrency;
nlapiLogExecution('DEBUG', 'Daily Exchange rate','url is '+url );
}
//nlapiLogExecution('DEBUG', 'Daily Exchange rate','herererererer url is '+url );
}
nlapiLogExecution('DEBUG', 'Daily Exchange rate','currency url is vname'+url );
url += '&date='+date1+'&api_key='+oandaApiKey
nlapiLogExecution('DEBUG', 'Daily Exchange rate','final url is '+url );
// By above code we are able to get the url, but Netsuite automatically converts " into " and we are not getting required url.
// URL which gets generated is :
// https://www.testname.com/rates/api/v1/rates/USD.csv?quote=EUR"e=USD"e=CAN"e=IND&date=2015/02/20
Avinish,
Have you tried to fetch the contents and then save it to the file system? Once there, you can open the file and process it. As per the documentation: “nlapiRequestURL automatically encodes binary content using base64 representation, since JavaScript is a character-based language with no support for binary types. This means you can take the contents returned and save them in the NetSuite file cabinet as a file or stream them directly to a response.”
By the way, if you seeking to get Bitcoin currency rates into NetSuite, it is available free of charge at https://www.btc4erp.com
Marty
Hi Marty,
Thanks for your valuable response…
I tried following solution.
Rather than using "e to create a url. i used %26quote and then while calling url thorugh nlapiRequestURL i replaced %26 with &.
It works…
Thanks…
Hello Marty, thanks for your code, I’ve been looking for how to update a record with restlet, your help can be useful, thanks
Hi Saul,
The way to update a record is like any other SuiteScript method to update records. Think of the RESTlet as a hookpoint function to gather inputs. The only difference is that there are four hookpoints corresponding with standard HTTP methods: GET, POST, PUT, and DELETE
Here is a code pattern that shows how to update records. In this case, creating a NetSuite journal entry:
https://blog.prolecto.com/2013/05/01/how-to-script-to-automate-netsuite-journal-entries/
Hi Marty,
thank you for your great tutorial here. Helps a lot. Especially that you are using perl code for your example is nice since we are using perl for our systems, too.
Hope for more!
Paul
Thanks Paul. As a systems integrator, we need to be able to to use different languages. Certainly, Perl is one of our older languages. Plenty more coming.
Marty
While trying to run this example, its giving error in perl script in accepting the password as:– it should require explicit package.plz resolve the same.
Hello Sir,
I want to know, how can I get data from reslet from external url.
The Restlet is an inbound to NetSuite technology. So your questions is sort of directionally confusing. However, you can call http services from NetSuite servers using the nlapiRequestURL (ver 1.0) call or use the N/http (ver 2.0) module.
This is great stuff. Can you share the JSON format for your code?
Does it look like this?
[
{
“trandate”: “12/01/2017”,
“customer”: “NetSuite IMP Project”,
“casetaskevent”: “SIT”,
“hours”: “5:00”,
“memo”: “Timesheet approval routing”,
}
]
This code example definitely could provide better instrumentation. If it succeeds, it does not return any business data.
I am trying the pull the data for a estimate record by hardcoding the value in restlet through restful web services,but i want to pass that hard code value in java.
How to pass the value in Java,can you please help me
Hi Harish,
The endpoint you create is up to your imagination. You can shape the data anyway you need to.
Marty
Hi,
Can i create a android application for warehouse management with netsuite using restlet api?
Hello Rajitha,
Yes, I don’t see why you would not be able to. It would be ambitious.
Marty