I'm developing a procedure that has to programatically create shipment for orders that are already paid and invoiced.

The problem is that even after creating the shipment, the order status remains to 'processing' instead of going to 'complete'. This does not happen if I manually Ship from the backend.

I found that the problem is that the quantity shipped for the order items is not updated, but remains 0 after saving the shipment and the order.

This is the procedure i'm using. No exception is issued, and shipment is correctly created.

    $orders = $this->_orderCollectionFactory->create()
        ->addFieldToFilter( 'entity_id' , array('in' => $ordersIdsArr) )
        ->setOrder('created_at', 'desc' );   

    foreach ($orders as $index => $order) {
        if ($order->canShip()) { 
           $shipment = $this->_convertOrder->toShipment($order);;
           $orderItems = $order->getItemsCollection()->addAttributeToSelect('*')->load();

           foreach ($orderItems as $orderItem) {
                if (! $orderItem->getQtyToShip() || $orderItem->getIsVirtual()) {
                $qtyShipped = $orderItem->getQtyToShip();
                $shipmentItem = $this->_convertOrder->itemToShipmentItem($orderItem)->setQty($qtyShipped);

            try {

                $saveTransaction = $this->_transactionFactory->create();
            } catch (\Exception $e) {



Any clue?

1 Answer 1


After struggling for 2 days on this, trying to understand what the problem was, studying in deep also Magento core classes for module-sales, I found someone on Magento community who had similar problems with Magento API and developed a patch.

The problem is from one year ago, but doesn't seem to have been addressed in subsequent versions of Magento, so I decided to adopt the same solution as the extension does, hence forcing the shipped quantity of order items to be equal to the quantity to ship, save order rows and then, save the order again. Well, it is just a patch and dunno if it is a general problem, but to me it has been the only way to make this work, and get finally the order in status of 'Complete'.

I added this after the first save of the order:

try {
        $saveTransaction = $this->_transactionFactory->create();

        $itemsCheck = $order->getItemsCollection()->addAttributeToSelect('*')->load();
        foreach ($itemsCheck as $item) {
            if (! $item->getQtyToShip() || $item->getIsVirtual()) { 
            $Norder = $shipment->getOrder()->load( $shipment->getOrder()->getId() );

Hope it can be of help for someone else.

  • 1
    Sissy you bloody legend :thumbsup:
    – Wildcard27
    Oct 6, 2017 at 3:23
  • always glad to help :)
    – sissy
    Oct 6, 2017 at 7:51
  • If shipping an arbitrary quantity and there are subsequent shipments for this order, it's important to increment the quantity instead of setting an absolute value: $orderItem->setQtyShipped($orderItem->getQtyShipped() + $shipmentQty);
    – micwallace
    Nov 5, 2019 at 6:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.