Setting Fields to NULL with NetSuite Web Services

I really like the new version of the NetSuite php Toolkit. It’s so much easier to use, and the syntax for working with various objects is streamlined and intuitive.

However, one issue I’ve run into is that all of the code examples and user discussions found in the NetSuite User Group web site refer to the older version of the toolkit. When I’ve run into cases where the new Toolkit differs from older versions, I have to really dig for the answers to my problems. One such example is setting fields to NULL in NetSuite SuiteTalk web services.

Setting fields to NULL using SuiteTalk is not a terribly well documented feature. For most objects in NetSuite, you can simply take a look at the Web Services Schema Browser for the object you are interested in, and you’ll find all the information you need for the various fields and child-record lists. One list you’ll never see mentioned is one called NullFieldList, probably because its not a child-record list like the other lists.

The NullFieldList is appropriately named – it’s a list of fields within a record you want to set to NULL. The NullFieldList is part of every Record object in NetSuite – it’s global across all Record Types. After a bit if trial and error, I finally have the NullFieldList figured out. Using the Inventory Item object, here’s how you use it with the new version of the php Toolkit:

1. Create a NullField object to hold your list (array) of fields you want to set to null.

$nfl = new NullField();

2. The NullField object takes an array of field internal IDs as its only property, called “name”.

$nfl->name = array(‘field1′, ‘field2′, ‘field3′);

3. Set the NullFieldList property of your record to be equal to the NullField object.

$item = new InventoryItem();
$item->internalId = “xxxxxx”;
$item->nullFieldList = $nfl;

After that, you just perform an update on your record like you would any other record in NetSuite.

 

NetSuite Customization Dos and Don’ts

One of the best, most awesome features of the NetSuite platform is the ability to easily customize it to meet your business needs. The customization features go far beyond simply adding a custom field here and there and updating custom data entry forms. You can also create entirely new add-on business applications that are integrated into the base ERP system. It’s truly amazing.

It’s also one of the most misused features of NetSuite.

I say this because the base, out-of-the-box functionality is very deep and well designed for several verticals – many, many businesses should be able to realize efficiencies and benefits with little or no customization at all. Of course, there are also many businesses that have unique business models and/or processes that add value to their organization where it makes sense to customize.

With this in mind, here are some dos and don’ts when customizing NetSuite.

DO – make an effort to thoroughly understand the functionality you are trying to create with a NetSuite customization. Similarly, you must thoroughly understand the base NetSuite features and functions. Can standard NetSuite support your business need? Or, can your business process be tweaked to make it a better fit within NetSuite?

DON’T – jump to a false conclusion that NetSuite does not support a perceived functional gap between your business requirements and proceed to embark on a complicated customization project. Perhaps a feature is not enabled within NetSuite, and once enabled, your functionality gap will be filled.

DO – conduct a cost/benefit analysis that compares the benefit of customization to the costs of customization (hint: costs can be measured in more ways than just out of pocket consulting fees). Even if it’s just a back of the napkin analysis, you may realize the “must have” functionality has costs that outweigh the benefits.

DON’T – hire an inexperienced consultant or programmer who hasn’t worked with NetSuite should you decide your functionality gap must be filled by customization. I’ve heard one person in the NetSuite development and consulting community call NetSuite the Ferrari of ERP systems. You wouldn’t take your Ferrari to Jiffy Lube for an oil change, would you? No. You take it to a Ferrari specialist.

DO – spend the money to purchase a NetSuite sandbox account for creating and testing your customizations. Developing in a production environment is a recipe for disaster. Furthermore, your sandbox is a perfect environment for training end users.

NetSuite Web Services Performance – Upsert vs UpsertList

I’m in the middle of a 400,000+ SKU data load in preparation for a NetSuite launch. I’ve chosen to use web services rather than CSV data imports due to the large number of items I need to get into NetSuite. Additionally, I have several custom record types with even more than 400,000 records to import. CSV just isn’t going to work for me.

I coded up some data imports using the phpToolkit, and used the upsert method for each record. Performance with the 1-record-per-call was adequate – anywhere from 1-3 seconds for an upsert. Out of curiosity around improving the upsert performance, I decided to batch up requests with an array of item records and call the upsertList method.

There is a limit of 100 records you can batch with any of the List methods, so I started there at the upper limit. Performance was better, but I had expected a bigger improvement than I was seeing, so I went to the other extreme – 10 records in a batch. This seemed to perform better than 100, with 2 upserts per second.  After trying 20, 30, then 40 records in a batch, I settled on 40 as the Suite spot, so to speak. I was able to finally get 4-6 records to process in 1 second.

My experiment is certainly not scientific – there are all sorts of variables that account for variances – the time of day the tests were run (nights are always better than days), varying networks the requests come from (my home office broadband isn’t shared by 100 other people in an office), etc.

Also, performing upserts on inventory items is much different than performing them on custom records, as custom records have no built-in logic to perform. Standard NetSuite objects may have all sorts of functionality to perform, so performance increases quite a bit on custom record types that have no scripting attached to them.

But basically, I’m sticking with my 40 records in a batch because the anecdotal evidence says that performs the best. If you see results that are different than mine, I’d love to hear about them.

Mystery of Related Items in NetSuite Web Site Solved!

Sometimes, I surprise myself. Over the course of my 20 year technology career, I’ve never really found a technical problem I couldn’t eventually figure out. I’ve become a master of the Google search and a freakishly thorough consumer of the NetSuite Users Group web site.

But while trying to work with the Related Items sublist within the Inventory Item object in NetSuite’s Web Services, “SuiteTalk”, I was almost ready to throw in the towel. I ran into a couple of problems:

1. The documentation for SuiteTalk is pretty thorough, and the SuiteTalk Schema Browser is ultra helpful in almost all situations.  However, the object representing the Related Items sublist, the PresentationItem object, is conspicuously absent from both.

2. The RecordRef object, one of the fundamental cores of SuiteTalk, is an object I know inside and out, just like any good SuiteTalk programmer.  However, when using it with this sublist, I was getting errors in the responses to my SuiteTalk requests.

I tried everything – I double and triple checked both my code and the data I was trying to process. But with no documentation for the PresentationItem object, I was at a bit of a loss.  Finally, I just decided to dig through the SuiteTalk WSDL and figure it out the old fashioned way.  Here’s what I found:

The Inventory Item is defined in the Accounting XSD. Inside the definition for Inventory Item, I found the related items sublist, which is called Presentation Items. After a bit of searching through the XSD, I finally found the PresentationItemList complex type, which is the container that holds the individual Presentation Items. I was hot on the trail! But this really doesn’t tell me much, other than the List will contain zero or more Presentation Items, and that you can specify whether to replace all the items in the list or just add to the ones already existing.

<complexType name=”PresentationItemList”>
<sequence>
<element name=”presentationItem” type=”platformCommon:PresentationItem” minOccurs=”0″ maxOccurs=”unbounded”/>
</sequence>
<attribute name=”replaceAll” type=”xsd:boolean” default=”true”/>
</complexType>
But the one piece of information here that was a huge help in my quest, was that the Presentation Item itself was defined in “platformCommon”. So I go and dig up the Common XSD. With a quick search of the Common XSD, there it was… The undocumented Presentation Item object!
<complexType name=”PresentationItem”>
<sequence>
<element name=”item” type=”platformCore:RecordRef” minOccurs=”0″/>
<element name=”itemType” type=”platformCommonTyp:PresentationItemType” minOccurs=”0″/>
<element name=”description” type=”xsd:string” minOccurs=”0″/>
<element name=”onlinePrice” type=”xsd:double” minOccurs=”0″/>
<element name=”basePrice” type=”xsd:double” minOccurs=”0″/>
</sequence>
</complexType>
And there’s that RecordRef object that I knew had to be there.  I coded up a loop that would read the ExternalIDs of items from the 3rd party system I was working with, and created a RecordRef for each one, appending the RecordRef to my array of RecordRefs as I went along. I also found the list of acceptible itemTypes, which the PresentationItem required.  I was all ready to test and rock and roll…
But NetSuite rejected my RecordRef objects, saying I could not use ExternalIDs.  How could this be??? I’ve used ExternalIDs with RecordRef objects 1000 times. I quickly coded up a GET statement using my RecordRef in order to grab the actual record from NetSuite. Then, I was able to get the InternalID of the record and use that in a new RecordRef object.
Once I plugged in that InternalID, BAM!  I had my Related Items sublist populating.  Here’s the PHP code for those who run into the same problem:

$itemItemList = new PresentationItemList();
$itemItemList->replaceAll = true;

sqlsrv_execute($dsItemItems);

$q = 0;
while ($dsItemItem = sqlsrv_fetch_object($dsItemItems)) {

$itemRR = new RecordRef();
$itemRR->externalId = $dsItemItem->ProdID;
$itemRR->type = “inventoryItem”;

$request = new GetRequest();
$request->baseRef = $itemRR;

$getResponse = $NSservice->get($request);
if ($getResponse->readResponse->status->isSuccess) {
$internalId = $getResponse->readResponse->record->internalId;
$itemRR = new RecordRef();
$itemRR->internalId = $internalId;
$itemRR->type = “inventoryItem”;

$itemItem = new PresentationItem();
$itemItem->item = $itemRR;
$itemItem->itemType = “_item”;

$itemItemList->presentationItem[] = $itemItem;
$q++;
}
}

print_r(“Processed ” . $q . ” related items\n”);

$item = new InventoryItem();
$item->externalId = $dsItem->externalId;
$item->presentationItemList = $itemItemList;

$request = new UpdateRequest();
$request->record = $item;
$upsertResponse = $NSservice->update($request);

Over the Air HDTV with Antennas Direct ClearStream 2V

I’ve written a bit about my cord cutting experience – ridding myself of $100+ DirecTV bill.  Part of my strategy included installing an HD antenna so that I could receive over the air HD programming for local channels.

I am happy to say that the Antennas Direct’s ClearStream 2V has been great.  I’m about 45 miles from downtown Chicago, and I receive all local channels well.  I even split the signal between 3 HD televisions in the house without an issue.

The only real issue I have has nothing to do with the antenna itself.  My original plan was to mount the antenna on the existing mast of the DirecTV dish on my roof.  The problem is that my roof line is very, very high – I don’t have a ladder tall enough to get up there! Plan B was to mount the antenna in the attic and connect to a run of coax that home runs into my structured wiring cabinet in the basement, where I would redistribute the signal to the 3 rooms that require it.  However, the coax was in an area I could not get to…

I settled on installing the antenna directly to the back of a TV on the upper level of my house, splitting the signal between the TV and running the other output back through coax in the wall. I was able to redistribute the signal as planned from the wiring cabinet in the basement.

I’m looking forward to watching the Chicago Bears in HD tomorrow without the use of a satellite box.

Roku Off to a Great Start

My cord cutting experiment to replace DirecTv with Roku is off to a nice start.  I ordered 2 Roku XD boxes over the weekend, and they arrived from California today!  Not too shabby. I thought I had a couple of HDMI cables laying around, but I thought wrong.  Will have to go buy a couple of short cables to get all boxes hooked up.

In my planning to cut the DirecTv cord, I also determined I needed to get local digital TV channels so I can watch the Chicago Bears games on Sunday afternoon.  I’ll be up on a ladder next weekend taking down my dish and attaching an HD antenna.  We’re almost there…

Next task is to find the programming the family wants and get all our iPhones/iPads loaded up with the Roku remote control app.  I think there is a significant opportunity to blog about finding content – what to watch, where to find it, sharing reviews and recommendations, etc.

I’m optimistic so far.

Cutting the DirecTv Cord – a Roku Experiment

OK. Gonna try to cut the DirecTV cord and get rid of $150/mo bill.

Step 1: Get Roku devices for each TV. I already have 1 for the family room – a good place to start.  There is a special this weekend for $20 off.

Step 2: Get an HD antenna and hook it up to the old mast that supported the satellite dish.

There’s structured wiring in my house for cable and internet, so no running cables to each room or using wi-fi. That’s a huge bonus.  The last thing I want is 3 TVs trying to stream via wi-fi while I’m trying to work.

Making the change to Roku/Over-the-air-HD should pay for itself in month #3.  This is the best solution for being able to watch local news, Bears games, and Bulls/Cubs/Sox games that are on WGN or WCIU.

If MLBtv ever gets its online act together so I can watch “local” games, maybe I buy an MLB subscription on the Roku if I get desperate. Since the NHL is kaput, I feel better about not having Comcast Sports Net this winter.

After 6 months of trying this, if it sucks, DirecTV can have me back by bribing me with a better deal on service.

To ToolKit or Not To ToolKit…

That is the question. Whether it’s nobler in your mind to struggle with the learning curve of SuiteTalk and the SuiteTalk Schema Browser, or to suffer the slings and arrows of those who say, “You’re using C#?!?”.

To REST. To REST.  SOAP no more. And by REST we end the heartache.

Everyone knows I’m a huge supporter and champion of the NetSuite platform.  There really isn’t anything you can’t do with it, whether you want to create custom functionality or integrate it with 3rd party systems, which I’ve done a ton of.  My method of choice for integrating other systems with NetSuite used to be SuiteTalk, their web services API.  And my programming language of choice was php using NetSuite’s php ToolKit.  I was self-taught, and had a great time pushing and pulling data all over the place.

However, it’s not easy to learn and deploy SuiteTalk applications.  This is not a complaint or criticism – every system of the size and complexity of NetSuite is going to require a significant learning curve. You’d expect the same from SAP, Oracle, or any other major ERP provider.

But that was then, before the days of NetSuite RESTLets.  It’s all so easy now.

RESTLets are custom REST interfaces to and from NetSuite, and they’re super slick and super easy to create.  If you know JavaScript and the SuiteScript library of NetSuite functions, you’re pretty much set to go.  And if you’re a php kind of person, throw in some standard and simple cURL functions, and you’ll be off to the races.  JSON or XML input and output – not a problem.

RESTLets are now my preferred integration option.

Two Level Deep Join in NetSuite Saved Searches

I love the saved search capabilities in NetSuite – there’s just a ton of functionality in this part of the platform, and you can do incredible things with it.

Generally, when you specify search criteria or results columns, the NetSuite saved search UI provides you with the database fields for the table you are searching, plus the database fields for directly joined tables.  But what do you do when you want to join another database table to the first joined table and use fields in that table for not only your result set, but as criteria as well? Using the fields in the result set is standard functionality, I had not really found a way to do this for search criteria until today.  Below is the basic data model I was working from.

The answer to the problem starts with creating additional custom fields on the Location record that reference fields in the Custom Record. I’m going to assume you already know how to join the Custom Record in the data model to the Location record – that’s fairly straight forward. But those fields in the Custom Record… how do you return those in your saved search?

Define custom field(s) on the Location record for the field(s) you’d like to access and make sure the “Store Value” check box is left unchecked.  You don’t want to store the field data along with the Location record, you only want to reference it. By utilizing the Source List and Source From fields on the Sourcing and Filtering tab on each custom field, you can define which field you want to reference.

Let’s say your newly created referenced custom field from the Custom Record is called “foo”.

Now, if and when the joined Custom Record is changed, the value of “foo” changes as well, and you can easily access this field in the saved search’s results, just like you can access any of the joined table fields.

However, using “foo” in the saved search’s criteria is another matter… Unlike in the search results tab, where the custom field is presented to you to choose from in the list of available join fields, you won’t find “foo” available for selection.  To get at that field, use a Formula field and cheat a bit.

The standard syntax for accessing joined fields in a formula looks like this – {table-name.fieldname}.  All you have to do is specify your custom field. So in our case, the field “foo” would be referenced as {location.foo}. You can then easily supply criteria as necessary.

In my use case, “foo” is a date, and I want to see all transactions where the Transaction Date (called “trandate” in the database) is less than the date in “foo”. I chose a Numeric Formula field and subtracted the Transaction Date from “foo” using “{location.foo}-{trandate}”, then specified “greather than 0″ for the result.

Worked like a charm.

 

A Vision for NetSuite eCommerce

Much has been made of NetSuite’s Suite Commerce, their new eCommerce offering.  My understanding is that the new framework addresses many of the issues that their eCommerce customers have raised over the years.  I say “my understanding” because I really can’t get my hands on the actual Suite Commerce product.  I can only repeat what NetSuite tells me (and everyone else) is in the new product offering:

  • Complete control of every pixel on every page
  • Completely customizable checkout – anything from a 1 page checkout to an AJAX powered checkout
  • Server-side scripting
  • Blazing fast page loads with help from the NetSuite CDN
  • Ability to design for all devices – iPhone, Android, iPad, etc.

But there’s still this black hole in my personal NetSuite knowledge base, a knowledge base that I think is probably one of the more robust here in Chicago, but maybe in the entire USA. I’m usually a fairly modest person, but I haven’t seen too many people that know more about NetSuite than I do (those working internally at NetSuite excluded, of course).  Anyway…  that black hole is Suite Commerce.

Perhaps NetSuite is playing their Suite Commerce cards very close to the vest while getting the product offering off the ground.  I can only speculate on that matter.  I can certainly see how the product would work if there was an architecture in place that allowed for creation of item category and item detail templates driven by a combination of HTML/JavaScript and a server-side pre-processor for embedded SuiteScript, much like you’d see in php, ASP, and other dynamic web languages.

Really, any sort of page could be created via this mechanism – it doesn’t have to be item or category related.  For example, multiple brick and mortar location retailers could generate a page that pulls locations from NetSuite and displays them on a map. For mobile users, it would be great if a store locator could access your current location and direct you to the nearest physical location.

It’s been almost a year and a half since I sat down with the NetSuite eCommerce folks and shared my wants and vision for my ideal NetSuite-enabled eCommerce platform, and it appears to be nearly within my grasp.