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.
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