Netsuite PHPToolkit Web Service - Search ItemFulfillment Tutorial

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

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);

By admin

your setPreferences comment really helped me out. thanks for posting ~

By pj (not verified)

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?

By aladar

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>

By admin

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.

By Scott Seong (not verified)

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.

$myNSclient->setPreferences(true, false, false, false, true, true);
$id = $this->getSalesOrderInternalId();

// Packaging Information
$packageFields = array (
		'packageWeight' => $data['packageWeight'],
		'packageDescr' => $data['packageDescr'],
		'packageTrackingNumber' => $data['packageTrackingNumber']
);
$package = new nsComplexObject('ItemFulfillmentPackage');
$package->setFields($packageFields);

$packageListFields = array ('package' => $package );
$packageList = new nsComplexObject('ItemFulfillmentPackageList');
$packageList->setFields($packageListFields);

//Record
$itemFulfillment = new nsComplexObject('ItemFulfillment');
$fulfillmentFields = array (
		'createdFrom'     =>    new nsRecordRef (
				array('internalId'=>$id, 'type' =>'salesOrder' )),
		'packageList'     =>    $packageList
);

$itemFulfillment->setFields($fulfillmentFields);
$response = $myNSclient->add($itemFulfillment);
By aladar

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/

By admin

Here is an example on how to use InitializeRecord to create an Invoice record.

// Create initializeRequest object
$initializeObject = new nsComplexObject("InitializeRecord");
$initializeObject->setFields(array("type"=>"invoice","reference"=>array(
    "type"=>"salesOrder","internalId"=>$internalid)));

// Call initializeResponse
$initializeResponse = $myNSclient->initialize($initializeObject);
       
$myNSclient->setPreferences(true,false);
       
// Create Invoice object
$addInvoice = new nsComplexObject("Invoice");
$addInvoice->setFields($initializeResponse->record->getFields());
       
// Create Invoice record through add operation
$addResponse = $myNSclient->add($addInvoice);

if (!$addResponse->isSuccess) {           

    echo "" . 
            $addResponse->statusDetail[0]->message . "";
               
    } else {
        echo "Successfully created invoice with internal ID ".
            $addResponse->recordRef->getField(internalId);           
}
By admin

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:


1.010.020.03

To accomplish this with PHP, we can initialize Item Fulfillment record from Sales Order like an example below:

$initializeObject = new nsComplexObject("InitializeRecord");
$initializeObject->setFields(array("type"=>"itemFulfillment",
		"reference"=>array("type"=>"salesOrder","internalId"=>$id)));
$initializeResponse = $myNSclient->initialize($initializeObject);
items = array();
if (!empty($initializeResponse->record)) {
	$orgItemList = @$initializeResponse->record->getField('itemList');
	$orgItems = @$orgItemList->getField('item');
	if (!is_array($orgItems)) {
		$orgItems = array($orgItems); // If single element, make an array
	}
	// Let's assume that we are only fulfilling 1st item.
	// and not fulfill 2nd item.
		$itemFields = array (
			'item' => $orgItems[0]->getField('item'),
			'orderLine' => $orgItems[0]->getField('orderLine'),
			'quantity' => 1
		);
		$item = new nsComplexObject('ItemFulfillmentItem');
		$item->setFields($itemFields);
		$items[] = $item;
		$itemFields = array (
			'item' => $orgItems[1]->getField('item'),
			'orderLine' => $orgItems[1]->getField('orderLine'),
			'quantity' => '0.0'
		);
		$item = new nsComplexObject('ItemFulfillmentItem');
		$item->setFields($itemFields);
		$items[] = $item;
	}
}

// Packaging Information
$packageFields = array (
		'packageWeight' => $data['packageWeight'],
		'packageDescr' => $data['packageDescr'],
		'packageTrackingNumber' => $data['packageTrackingNumber']
);
$package = new nsComplexObject('ItemFulfillmentPackage');
$package->setFields($packageFields);

$packageListFields = array ('package' => $package );
$packageList = new nsComplexObject('ItemFulfillmentPackageList');
$packageList->setFields($packageListFields);

//Record
$itemFulfillment = new nsComplexObject('ItemFulfillment');
$fulfillmentFields = array (
		'createdFrom'     =>    new nsRecordRef (
				array('internalId'=>$id, 'type' =>'salesOrder' )),
		'packageList'     =>    $packageList,
);
if (!empty($items)) {
	echo "NOT EMPTY ITEM LIST";
	$itemListFields = array ('item' => $items );
	$itemList = new nsComplexObject('ItemFulfillmentItemList');
	$itemList->setFields($itemListFields);
	$fulfillmentFields['itemList'] = $itemList;
}

$itemFulfillment->setFields($fulfillmentFields);
$response = $myNSclient->add($itemFulfillment);
By admin

It should be noted that the quantity value must be enclosed in a quote like below.

$itemFields = array (
        'orderLine' => $orgItems->getField('orderLine'),
        'quantity' => '0.0'
);

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.

$itemFields = array (
        'orderLine' => $orgItems->getField('orderLine'),
        'quantity' => 0
);
By admin

Add new comment

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.