Magento Orders Shipment – Programmatically Creating Shipments

magento-1.7ordersshipment

I came across different ways to create shipment programmatically. They are

     //Type 1
     $converter=Mage::getModel('sales/convert_order');
     $shipment=$converter->toShipment($order);
     // snip

     //Type 2
     $shipment = Mage::getModel('sales/service_order', $order)
                                ->prepareShipment($this->_getItemQtys($order));
     // snip

     //Type 3
     $shipment = Mage::getModel('sales/service_order', $order)->prepareShipment($itemQty);
     $shipment = new Mage_Sales_Model_Order_Shipment_Api();
     $shipmentId = $shipment->create($orderId);
     // snip

What are the difference between these methods.Out of the three methods which is proper method to create shipments and add tracking numbers.

Best Answer

I'll give it a shot. Let's take them one at a time:

Method 1

$converter=Mage::getModel('sales/convert_order');
$shipment=$converter->toShipment($order);

$converter above is loaded from the class Mage_Sales_Model_Convert_Order, which uses a core helper called copyFieldset to copy order details into a shipment object. $order has to be of type array or Varien_Object.

This method is actually at the core of Method 3, as it uses Mage::getModel('sales/convert_order') in its constructor call.

Key differentiator of this method - it can take an array or an object $order and generate a basic $shipment object. It is a lower-level method used exclusively by the methods you put forth in Method 2, Method 3.

Method 2

 $shipment = Mage::getModel('sales/service_order', $order)
                            ->prepareShipment($this->_getItemQtys($order));

This seems to be the most popular way in Magento's Core of generating a shipment as it is used in both Shipment and Invoice controllers. $order is used as a constructor argument to the instantiation of Mage_Sales_Model_Service_Order, setting it as a protected property on the object.

You're then calling prepareShipment and passing a quantity. As this method uses the converter class from Method 1, you needn't specify more details such as order items pass item shipment qty details in the prepareShipment argument, called here with $this->_getItemQtys. To use this on your own context, all you need to do is pass the quantity of items in an array with the following format:

array(
  'order_item_id'=>$qty,
  'order_item_id'=>$qty,
  'order_item_id'=>$qty
)

Key differentiator of this method - it gives you back a $shipment object, but with all items converted on it. It's plug-and-play.

Method 3

I could not find evidence of using this method in the Core. It looks like a hack, to be honest. Here's the method:

$itemQty =  $order->getItemsCollection()->count();
$shipment = Mage::getModel('sales/service_order', $order)->prepareShipment($itemQty);
$shipment = new Mage_Sales_Model_Order_Shipment_Api();
$shipmentId = $shipment->create($orderId);

Step 1 is exactly the same as Method 2 above. No difference. However, you get back a $shipment object, which is replaced by a direct insantiation of Mage_Sales_Model_Order_Shipment_Api. This is non-standard. The best-practice way of getting a shipment Api object would be to call Mage::getModel('sales/order_shipment_api').

Next, it uses that overwritten, new Shipment API object to create a shipment from an $orderId variable that hasn't been defined in your code. Again, this seems like a workaround.

Looking at Mage_Sales_Model_Order_Shipment_Api::create(), it seems like a one-stop-shop for generating a shipment as the most basic details needed to create the shipment is only an order increment_id.

This is a hack that shouldn't be used by any module or extension. This API is meant to be consumed by features exposed via XML RPC / SOAP API requests and is intentionally basic to eliminate multiple step API requests.

Eventually Method 3 gets to the nitty-gritty, though, and via a call to Mage_Sales_Model_Order, it calls prepareShipment, which is a higher-order abstraction for the familiar Method 2 above:

public function prepareShipment($qtys = array())
{
    $shipment = Mage::getModel('sales/service_order', $this)->prepareShipment($qtys);
    return $shipment;
}

Key differentiator here - if you need a shipment, don't mind hacks, and only have an increment_id - use this method. Also useful information if you prefer to handle this via the SOAP API.

I hope that helps.