Magento Shipment Creation

Magento shipment creation occurs when a merchant, in the Magento admin, heads to Sales -> Orders, selecting an order and clicking the Ship button.

The Ship button can be seen or not seen due to the following code in the Mage_Adminhtml_Block_Sales_Order_View class.

if ($this->_isAllowedAction('ship') && $order->canShip()
    && !$order->getForcedDoShipmentWithInvoice()) {
    $this->_addButton('order_ship', array(
        'label'     => Mage::helper('sales')->__('Ship'),
        'onclick'   => 'setLocation(\'' . $this->getShipUrl() . '\')',
        'class'     => 'go'
    ));
}

The canShip() method belongs in the main sales order model.

public function canShip()
{
    if ($this->canUnhold() || $this->isPaymentReview()) {
        return false;
    }

    if ($this->getIsVirtual() || $this->isCanceled()) {
        return false;
    }

    if ($this->getActionFlag(self::ACTION_FLAG_SHIP) === false) {
        return false;
    }

    foreach ($this->getAllItems() as $item) {
        if ($item->getQtyToShip()>0 && !$item->getIsVirtual()
            && !$item->getLockedDoShip())
        {
            return true;
        }
    }
    return false;
}

Here we can see the checks Magento goes through to determine whether a shipment should be created or not.

When we go to create a shipment and ship the contents of an order, we hit the saveAction() method of the ShipmentController.php class.

public function saveAction()
{
    $data = $this->getRequest()->getPost('shipment');
    if (!empty($data['comment_text'])) {
        Mage::getSingleton('adminhtml/session')->setCommentText($data['comment_text']);
    }

    try {
        $shipment = $this->_initShipment();
        if (!$shipment) {
            $this->_forward('noRoute');
            return;
        }

        $shipment->register();
        $comment = '';
        if (!empty($data['comment_text'])) {
            $shipment->addComment(
                $data['comment_text'],
                isset($data['comment_customer_notify']),
                isset($data['is_visible_on_front'])
            );
            if (isset($data['comment_customer_notify'])) {
                $comment = $data['comment_text'];
            }
        }

        if (!empty($data['send_email'])) {
            $shipment->setEmailSent(true);
        }

        $shipment->getOrder()->setCustomerNoteNotify(!empty($data['send_email']));
        $responseAjax = new Varien_Object();
        $isNeedCreateLabel = isset($data['create_shipping_label']) && $data['create_shipping_label'];

        if ($isNeedCreateLabel && $this->_createShippingLabel($shipment)) {
            $responseAjax->setOk(true);
        }

        $this->_saveShipment($shipment);

        $shipment->sendEmail(!empty($data['send_email']), $comment);

        $shipmentCreatedMessage = $this->__('The shipment has been created.');
        $labelCreatedMessage    = $this->__('The shipping label has been created.');

        $this->_getSession()->addSuccess($isNeedCreateLabel ? $shipmentCreatedMessage . ' ' . $labelCreatedMessage
            : $shipmentCreatedMessage);
        Mage::getSingleton('adminhtml/session')->getCommentText(true);
    }

    ....

Similar to invoices, we call an _init() method, which in this case is _initShipment() which in turn calls the prepareShipment() method.

When we return back to the saveAction() method, we eventually call the _saveShipment() method that adds the shipment and order objects to the transaction model.

protected function _saveShipment($shipment)
{
    $shipment->getOrder()->setIsInProcess(true);
    $transactionSave = Mage::getModel('core/resource_transaction')
        ->addObject($shipment)
        ->addObject($shipment->getOrder())
        ->save();

    return $this;
}

Magento allows you to create many shipments for an order with items which can ship separately. With each shipment, the shipment items can be obtained from the getAllItems() method.

public function getAllItems()
{
    $items = array();
    foreach ($this->getItemsCollection() as $item) {
        if (!$item->isDeleted()) {
            $items[] =$item;
        }
    }
    return $items;
}
public function getItemsCollection()
{
    if (empty($this->_items)) {
        $this->_items = Mage::getResourceModel('sales/order_shipment_item_collection')->setShipmentFilter($this->getId());
         if ($this->getId()) {
             foreach ($this->_items as $item) {
                 $item->setShipment($this);
             }
        }
    }
    return $this->_items;
}

You may have noticed that when in the Sales -> Orders section and creating a shipment, you can add a tracking number to the order.

public function getAllTracks()
{
    $tracks = array();
    foreach ($this->getTracksCollection() as $track) {
        if (!$track->isDeleted()) {
            $tracks[] =$track;
        }
    }
    return $tracks;
}

Shipping carriers that come with the default Magento installation such as DHL and FedEx allow the customer to track the shipment online.

Using the getNumberDetail() method, we can get specific tracking information.

public function getNumberDetail()
{
    $carrierInstance = Mage::getSingleton('shipping/config')->getCarrierInstance($this->getCarrierCode());
    if (!$carrierInstance) {
        $custom['title'] = $this->getTitle();
        $custom['number'] = $this->getNumber();
        return $custom;
    } else {
        $carrierInstance->setStore($this->getStore());
    }
    if (!$trackingInfo = $carrierInstance->getTrackingInfo($this->getNumber())) {
        return Mage::helper('sales')->__('No detail for number "%s"', $this->getNumber());
    }
    return $trackingInfo;
}

Magento stores shipment information in a few different tables.

  • sales_flat_shipment – stores main information of shipment.
  • sales_flat_shipment_comment – stores comments for a shipment.
  • sales_flat_shipment_item – stores list of items of shipment.
  • sales_flat_shipment_grid – stores some information of shipment to increase loading speed when showing a grid invoice.
  • sales_flat_shipment_track – stores tracking information for a shipment.

Note: This article is based on Magento Community/Open Source version 1.9.