3

I want to create shipment + Invoice using mass action in order grid.

Can anyone tell me the approach to do this? Share code if possible otherwise approach would be good.

Thanks.

  • 1
    Did you search for any tutorial? – Shoaib Munir Mar 15 at 11:24
3

I recently worked on this feature both (Invoice and Shipment). First you have to add Uicomponent in your module

Path: app\code\Vendor\Module\view\adminhtml\ui_component\sales_order_grid.xml

<?xml version="1.0" encoding="UTF-8"?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <listingToolbar name="listing_top">
            <massaction name="listing_massaction">
<action name="order_invoice">
                    <argument name="data" xsi:type="array">
                        <item name="config" xsi:type="array">
                            <item name="type" xsi:type="string">order_mass</item>
                            <item name="label" xsi:type="string" translate="true">Invoice</item>
                            <item name="url" xsi:type="url" path="ordermass/order/massInvoice"/>
                            <item name="confirm" xsi:type="array">
                                <item name="title" xsi:type="string" translate="true">Invoice</item>
                                <item name="message" xsi:type="string" translate="true">Are you sure you want to create invoice for selected items?</item>
                            </item>
                        </item>
                    </argument>
                </action>   
                <action name="order_ship">
                    <argument name="data" xsi:type="array">
                        <item name="config" xsi:type="array">
                            <item name="type" xsi:type="string">order_ship_mass</item>
                            <item name="label" xsi:type="string" translate="true">Ship (Email)</item>
                            <item name="url" xsi:type="url" path="ordermass/order/massShip"/>
                            <item name="confirm" xsi:type="array">
                                <item name="title" xsi:type="string" translate="true">Ship (Email)</item>
                                <item name="message" xsi:type="string" translate="true">Are you sure you want to create shipment for selected items?</item>
                            </item>
                        </item>
                    </argument>
                </action>
</massaction>
    </listingToolbar>
</listing>

After that Add routes in your module Path: app\code\Vendor\Module\etc\adminhtml\routes.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="admin">
        <route id="ordermass" frontName="ordermass">
            <module name="Vendor_Module" />
        </route>
    </router>
</config>

For Invoice Controller

Path: app\code\Vendor\Module\Controller\Adminhtml\Order\MassInvoice.php

<?php

namespace Vendor\Module\Controller\Adminhtml\Order;

use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
use Magento\Backend\App\Action\Context;
use Magento\Ui\Component\MassAction\Filter;
use Magento\Sales\Model\ResourceModel\Order\CollectionFactory;
use Magento\Sales\Api\OrderManagementInterface;

class MassInvoice extends \Magento\Sales\Controller\Adminhtml\Order\AbstractMassAction
{

    protected $orderManagement;
    /**
     * @var \Magento\Sales\Model\Service\InvoiceService
     */
    protected $_invoiceService;

    /**
     * @var \Magento\Framework\DB\Transaction
     */
    protected $_transaction;

    public function __construct(
        Context $context,
        Filter $filter,
        CollectionFactory $collectionFactory,
        OrderManagementInterface $orderManagement,
        \Magento\Sales\Model\Service\InvoiceService $invoiceService,
        \Magento\Framework\DB\Transaction $transaction,
        \Magento\Backend\Model\Auth\Session $authSession
    ) {
        parent::__construct($context, $filter);
        $this->authSession = $authSession;
        $this->collectionFactory = $collectionFactory;
        $this->orderManagement = $orderManagement;
        $this->_invoiceService = $invoiceService;
        $this->_transaction = $transaction;
    }

    protected function massAction(AbstractCollection $collection)
    {
        $countInvoiceOrder = 0; $NonInvoiceOrdernuumbers = '';
        $model = $this->_objectManager->create('Magento\Sales\Model\Order');

        $username = $this->authSession->getUser()->getUsername();
        $appendusername = "(".$username.")";

        foreach ($collection->getItems() as $order) {
            if (!$order->getEntityId()) {
                continue;
            }
            $loadedOrder = $model->load($order->getEntityId());

            if($loadedOrder->canInvoice()) {

            // Create invoice for this order
            $invoice = $this->_objectManager->create('Magento\Sales\Model\Service\InvoiceService')->prepareInvoice($loadedOrder);
            //$invoice->getOrder()->setIsInProcess(true);
            $invoice->setShippingAmount($loadedOrder->getShippingAmount());
            $invoice->setBaseShippingAmount($loadedOrder->getBaseShippingAmount());
            $invoice->setTaxAmount($loadedOrder->getTaxAmount());
            $invoice->setBaseTaxAmount($loadedOrder->getBaseTaxAmount());
            $invoice->setSubtotal($loadedOrder->getSubtotal());
            $invoice->setBaseSubtotal($loadedOrder->getBaseSubtotal());
            $invoice->setGrandTotal($loadedOrder->getGrandTotal());
            $invoice->setBaseGrandTotal($loadedOrder->getBaseGrandTotal());

            // Register as invoice item
            $invoice->setRequestedCaptureCase(\Magento\Sales\Model\Order\Invoice::CAPTURE_OFFLINE);
            $invoice->register();
            // Save the invoice to the order
            $transaction = $this->_objectManager->create('Magento\Framework\DB\Transaction')
            ->addObject($invoice)
            ->addObject($invoice->getOrder());
            $transaction->save();

            //send notification code
            $loadedOrder->addStatusHistoryComment(
                __('Notified customer about invoice #%1. '.$appendusername, $invoice->getId())
            )->setIsCustomerNotified(false)->save();

            if ($loadedOrder->canShip()) {
            $loadedOrder->setState(\Magento\Sales\Model\Order::STATE_PROCESSING, true);
            $loadedOrder->setStatus(\Magento\Sales\Model\Order::STATE_PROCESSING);
            $loadedOrder->addStatusToHistory($loadedOrder->getStatus(), 'Order status set to processing using Mass Invoice action '.$appendusername);
            $loadedOrder->save();
            }

            $countInvoiceOrder++;
            }
            else {
                if (empty($NonInvoiceOrdernuumbers)){
                $NonInvoiceOrdernuumbers = $NonInvoiceOrdernuumbers.$loadedOrder->getIncrementId();
                }
                else{
                $NonInvoiceOrdernuumbers = $NonInvoiceOrdernuumbers.", ".$loadedOrder->getIncrementId();    
                }
            }
        }
        $countNonInvoiceOrder = $collection->count() - $countInvoiceOrder;

        if ($countNonInvoiceOrder && $countInvoiceOrder) {
            $this->messageManager->addSuccess(__('%1 order(s) Invoice created successfully.', $countInvoiceOrder));
            $this->messageManager->addError(__('Invoice already created for %1 order(s).', $NonInvoiceOrdernuumbers));
        } elseif ($countNonInvoiceOrder) {
            $this->messageManager->addError(__('Invoice already created for %1 order(s).', $NonInvoiceOrdernuumbers));
        }

        if ($countInvoiceOrder) {
            $this->messageManager->addSuccess(__('%1 order(s) Invoice created successfully.', $countInvoiceOrder));
        }

        $resultRedirect = $this->resultRedirectFactory->create();
        $resultRedirect->setPath($this->getComponentRefererUrl());
        return $resultRedirect;
    }
}

For Shipment Controller

Path: app\code\Vendor\Module\Controller\Adminhtml\Order\MassShip.php

<?php

namespace Vendor\Module\Controller\Adminhtml\Order;

use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
use Magento\Backend\App\Action\Context;
use Magento\Ui\Component\MassAction\Filter;
use Magento\Sales\Model\ResourceModel\Order\CollectionFactory;
use Magento\Sales\Api\OrderManagementInterface;

class MassShip extends \Magento\Sales\Controller\Adminhtml\Order\AbstractMassAction
{

    protected $orderManagement;

    public function __construct(
        Context $context,
        Filter $filter,
        CollectionFactory $collectionFactory,
        OrderManagementInterface $orderManagement,
        \Magento\Backend\Model\Auth\Session $authSession

    ) {
        parent::__construct($context, $filter);
        $this->authSession = $authSession;
        $this->collectionFactory = $collectionFactory;
        $this->orderManagement = $orderManagement;
    }

    protected function massAction(AbstractCollection $collection)
    {
        $countShipOrder = 0; $NonShipOrdernuumbers = '';
        $model = $this->_objectManager->create('Magento\Sales\Model\Order');

        $username = $this->authSession->getUser()->getUsername();
        $appendusername = "(".$username.")";

        foreach ($collection->getItems() as $order) {
            if (!$order->getEntityId()) {
                continue;
            }
            $loadedOrder = $model->load($order->getEntityId());

            if($loadedOrder->canShip()) {


            $convertOrder = $this->_objectManager->create('Magento\Sales\Model\Convert\Order');
            $shipment = $convertOrder->toShipment($loadedOrder);

            // Loop through order items
            foreach ($order->getAllItems() AS $orderItem) {
                // Check if order item has qty to ship or is virtual
                if (! $orderItem->getQtyToShip() || $orderItem->getIsVirtual()) {
                    continue;
                }
                $qtyShipped = $orderItem->getQtyToShip();
                // Create shipment item with qty
                $shipmentItem = $convertOrder->itemToShipmentItem($orderItem)->setQty($qtyShipped);
                // Add shipment item to shipment
                $shipment->addItem($shipmentItem);
            }

            // Register shipment
            $shipment->register();
            $shipment->getOrder()->setIsInProcess(true);

            try {
                // Save created shipment and order
                $shipment->save();
                $shipment->getOrder()->save();
                //send notification code
                $loadedOrder->addStatusHistoryComment(
                __('Notified customer about shipment #%1. '.$appendusername, $shipment->getId())
                )->setIsCustomerNotified(false)->save();

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

                // Send email
                $this->_objectManager->create('Magento\Shipping\Model\ShipmentNotifier')
                    ->notify($shipment);

                $shipment->save();
            } catch (\Exception $e) {
                $this->messageManager->addError(__($e->getMessage()));
            }

            if ($loadedOrder->canInvoice()) {
            $loadedOrder->setState(\Magento\Sales\Model\Order::STATE_PROCESSING, true);
            $loadedOrder->setStatus('shipped');
            $loadedOrder->addStatusToHistory('shipped', 'Order status set to shipped using Mass Ship action. '.$appendusername);
            $loadedOrder->save();
            }

            $countShipOrder++;
            }
            else {
                if (empty($NonShipOrdernuumbers)){
                $NonShipOrdernuumbers = $NonShipOrdernuumbers.$loadedOrder->getIncrementId();
                }
                else{
                $NonShipOrdernuumbers = $NonShipOrdernuumbers.", ".$loadedOrder->getIncrementId();  
                }
            }
        }
        $countNonShipOrder = $collection->count() - $countShipOrder;

        if ($countNonShipOrder && $countShipOrder) {
            $this->messageManager->addSuccess(__('%1 order(s) Shipment created successfully.', $countShipOrder));
            $this->messageManager->addError(__('Shipment already created for %1 order(s).', $NonShipOrdernuumbers));
        } elseif ($countNonShipOrder) {
            $this->messageManager->addError(__('Shipment already created for %1 order(s).', $NonShipOrdernuumbers));
        }

        if ($countShipOrder) {
            $this->messageManager->addSuccess(__('%1 order(s) Shipment created successfully.', $countShipOrder));
        }

        $resultRedirect = $this->resultRedirectFactory->create();
        $resultRedirect->setPath($this->getComponentRefererUrl());
        return $resultRedirect;
    }
}

This is tested code, I hope this will help

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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