5

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
  • 1
    Did you search for any tutorial? Commented Mar 15, 2019 at 11:24

1 Answer 1

6

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

2
  • $collection->getItems() not working for me when i am trying to do mass action Commented Jan 30, 2020 at 9:58
  • I tested the code in 2.3.5-p1 but its not working. The mass action should appear in the actions dropdown in the "Orders" page, right?! Commented Jan 6, 2021 at 12:17

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.