Our business require that we retrieve Quantum View data from UPS, and create or edit ItemFulfillment records with PackageList and Packages. As described in the earlier articles, we've created a UPS developer access key and setup required Quantum View subscriptions for later retrieval of Quantum View events.
I've spent great amount of time trying to figure out how to retrieve ItemFulfillment record(s) associated with a particular Sales Order. After spending more than a day, I've found a solution after looking through the C# examples. Netsuite documentations on how to use PHPToolKit is somewhat premature, and it requires a lot of digging and trial-and-error to figure out a solution. For those who are new to NetSuite, you may want to check out NetSuite Developer Resources I have compiled for easy reference.
require_once ('PHPtoolkit.php'); require_once ('login_info.php'); class ItemFulfillment { private $so = null; private $data = null; function __construct($soId) { $this->soId = $soId; $this->so = $this->getSalesOrder($soId); } // Retrieve a Sales Order record from NetSuite from the // given Sales Order number. private function getSalesOrder($soId) { global $myNSclient; $id = 0; $myNSclient->setSearchPreferences(false, 10); $params = new nsComplexObject('SearchStringField'); $params->setFields(array('searchValue' => $soId, 'operator' => 'is')); $search = new nsComplexObject("TransactionSearchBasic"); $search->setFields(array ('tranId'=> $params)); $response = $myNSclient->search($search); if ($response->isSuccess) { // We are expecting at most 1 record from this search. $so = $response->recordList[0]; } else { $so = null; } return $so; } public function getSalesOrderId() { if ($this->so === null) { return null; } else { return $this->so->getField('tranId'); } } private function getSalesOrderInternalId() { if ($this->so === null) { return null; } else { return $this->so->getField('internalId'); } } /** * Retrieve the ItemFulfillment record(s) attached to a Sales Order. * */ public function getFromSalesOrder() { global $myNSclient; $id = $this->getSalesOrderInternalId(); $myNSclient->setSearchPreferences(false, 10); $search = new nsComplexObject("TransactionSearchBasic"); $type = new nsComplexObject('SearchEnumMultiSelectField'); $type->setFields(array('searchValue' => array('_itemFulfillment'), 'operator' => 'anyOf')); $so = new nsRecordRef(array('type' => 'salesOrder', 'internalId' => $id)); $createdFrom = new nsComplexObject('SearchMultiSelectField'); $createdFrom->setFields(array('searchValue' => $so, 'operator' => 'anyOf')); $search->setFields(array ('type' => $type, 'createdFrom' => $createdFrom)); $response = $myNSclient->search($search); //print_r($response); return $response->recordList; } /** * Retrieve a ItemFulfillment record from an Internal ID. * * @param String $id -- Internal ID of the ItemFulfillment record */ public function get($id) { global $myNSclient; $recordRef = new nsRecordRef(array('internalId' => $id, 'type' => 'itemFulfillment')); $response = $myNSclient->get($recordRef); return $response->record; } /** * Return packages. * * @param integer $id - ItemFulfillment Internal ID * returns an array of nsComplexObject('ItemFulfillmentPackage'). */ public function getPackages($id) { global $myNSclient; $itemFulfillment = $this->get($id); $packages = null; if ($itemFulfillment != null) { $packageList = $itemFulfillment->getField('packageList'); if ($packageList != null) { $packages = $packageList->getField('package'); } } return $packages; } /** * Add an ItemFulfillment record with Package Info into NetSuite. */ public function add() { global $myNSclient; $id = $this->getSalesOrderInternalId(); echo "Internal ID: " . $id . "\n"; // Package Info $packageFields = array ( 'packageWeight' => 1.0, 'packageTrackingNumber' => '1Z00111122223333' ); $package = new nsComplexObject('ItemFulfillmentPackage'); $package->setFields($packageFields); $packageListFields = array ('package' => $package ); $packageList = new nsComplexObject('ItemFulfillmentPackageList'); $packageList->setFields($packageListFields); // ItemFulfillment Record $itemFulfillment = new nsComplexObject('ItemFulfillment'); $fulfillmentFields = array ( 'createdFrom' => new nsRecordRef ( array('internalId'=>$id, 'type' =>'salesOrder' )), 'packageList' => $packageList ); $itemFulfillment->setFields($fulfillmentFields); $myNSclient->setPreferences(true, false, false, false, true, true); $response = $myNSclient->add($itemFulfillment); if ($response->isSuccess) return true; else{ return false; } } }
For those who may be interested in looking at C# examples, here is the code snippets offered as help.
Retrieving all ItemFulfillment records from NetSuite.
TransactionSearch transactions = new TransactionSearch(); TransactionSearchBasic transactionsBasic = new TransactionSearchBasic(); // Set transaction type SearchEnumMultiSelectField searchItemFullfillmentField = new SearchEnumMultiSelectField(); searchItemFullfillmentField.@operator = SearchEnumMultiSelectFieldOperator.anyOf; searchItemFullfillmentField.operatorSpecified = true; searchItemFullfillmentField.searchValue = new String[] { "_itemFulfillment" }; transactionsBasic.type = searchItemFullfillmentField; transactions.basic = transactionsBasic; SearchResult searchResult = _service.search(transactions);
Searching for a particular ItemFulfillment record associated with a Sales Order.
RecordRef so = new RecordRef(); so.internalId = "Your_SO_Id"; so.type = RecordType.salesOrder; so.typeSpecified = true; SearchMultiSelectField createdFrom = new SearchMultiSelectField(); createdFrom.@operator = SearchMultiSelectFieldOperator.anyOf; createdFrom.operatorSpecified = true; createdFrom.searchValue = new RecordRef[] {so}; transactionsBasic.createdFrom = createdFrom;
Comments
Re: INSUFFICIENT_PERMISSION
The field "subtotal" is readonly and you cannot set a value for that field. You may set the ignoreReadOnlyFields preference to true and you will not get the error message.
$myNSclient->setPreferences(true, false, false, false, true, true);
Thanks!
your setPreferences comment really helped me out. thanks for posting ~
Add a package to ItemFulfillment > Unexpected error
I am trying to add a package to an existing ItemFulfillment, and ItemFulfillmentPackageList via PHPToolkit. However, I'm getting an error that doesn't provide much information.
What I am doing is working from a retrieved ItemFulfillment record, and adding a package to the PackageList. There are a few scenarios.
1. ItemFulfillment exists, but no packageList exists. In this case, I'll create a packageList and package. In this case do I update the ItemFulfillment record, or PackageList record?
2. If packageList exists with other packages, and I'm adding one more package to existing packages. What would be to procedure for doing this?
Re: Add a package to ItemFulfillment > Unexpected error
Please take a look at the SOAP request XML attached. On your code, you have to load the Item Fulfillment record. Then, you have to check the packageList count. If the packageList count is zero (0), the follow the first sample when you add a new package on the sublist.
<pre>
//if no packageList
<platformMsgs:update>
<platformMsgs:record xsi:type="s0:ItemFulfillment" internalId="5755">
<s0:packageList>
<s0:package>
<s0:packageWeight>150.0</s0:packageWeight>
<s0:packageTrackingNumber>1Z00000058K24921A</s0:packageTrackingNumber>
</s0:package>
</s0:packageList>
</platformMsgs:record>
</platformMsgs:update>
//if packageList exists
<platformMsgs:update>
<platformMsgs:record xsi:type="s0:ItemFulfillment" internalId="5755">
<s0:packageList replaceAll="false">
<s0:package>
<s0:packageWeight>100.0</s0:packageWeight>
<s0:packageTrackingNumber>1Z00000058K24921A</s0:packageTrackingNumber>
</s0:package>
</s0:packageList>
</platformMsgs:record>
</platformMsgs:update>
</pre>
Re: Add a package to ItemFulfillment > Unexpected error
If you create a writable "nslog" folder, the request and response xml files are written there. For updating a package record, you'll have to create a new object, assign internalID of the package and run update. Working from the existing record will not work.
Adding an Item Fulfillment causes INVALID_INITIALIZE_REF
We have a sales order with an Item Fulfillment record. Trying to create a second Item Fulfillment record on the same Sales Order causes the following error:
ERROR: INVALID_INITIALIZE_REF
Message: You have an invalid sales order 18506 or the order is already closed.
Do you not recommend creating multiple Item Fulfillment record off of a single Sales Order?
Here is the code snippet used to insert a new Item Fulfillment record with SuiteTalk.
ERROR: INVALID_INITIALIZE_REF
According to Netsuite support:
You could include all of the line items in your SOAP request, and just set the quantity of the items that will not be fulfilled to zero.
Also, explore using the Initialize operation as this mimics the behavior in the UI. The best practice for creating an item fulfillment in Web services is to use the initialize operation, as it returns the item fulfillment with all the defaults set and users will not have to specify the item quantities themselves. (kindly review the response returned by the Initialize operation first)
There are plenty of helpful articles with sample codes regarding this in the SuiteAnswers site and the Help Guide. Here are some links:
Using initialize, then add in PHP -> https://netsuite.custhelp.com/app/answers/detail/a_id/22248/kw/initialize
Item Fulfillment in PHP -> https://netsuite.custhelp.com/app/answers/detail/a_id/19761/kw/
Item Fulfillment in Web Services -> https://netsuite.custhelp.com/app/answers/detail/a_id/10931/kw/
PHP Web Service InitializeRecord example (invoice)
Here is an example on how to use InitializeRecord to create an Invoice record.
Sales Order is already closed -- can't create Item Fulfillment
By creating an ItemFulfillment record without specifying item list with quantity fulfilled, Netsuite automatically assume that the order is fulfilled entirely. This is the reason the 2nd item fulfillment record cannot be created, and the INVALID_INITIALIZE_REF error. To circumvent this issue, we need to specify quantity of each line item fulfilled so that the order is partially fulfilled if not all line items are fulfilled.
Partial fulfillment is achieved by creating an Item Fulfillment record with InitializeRecord operation, and assign quantity of the items that will not be fulfilled to zero. Here is the desired request.xml file:
To accomplish this with PHP, we can initialize Item Fulfillment record from Sales Order like an example below:
Assigning Item fulfilled quantity to zero
It should be noted that the quantity value must be enclosed in a quote like below.
If you do not enclose quantity value in a quote like below, the quantity will not be included in the request.xml file and the entire quantity will be fulfilled.
Add new comment