Since there's still no default way in Magento 2.2.x to accomplish this, I've created a basic mass action that will create an invoice for each selected order, which:
- Will be set to "Capture offline"
- Change the order status to "Processing"
- (Optional) Sends out the invoice e-mail (see commented code)
Of course this can also be extended upon to meet your wishes.
Assuming that you know how to create a custom module (Remember to change all instances of Vendor and Module with your own module details):
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="vendor_module_mass_invoice">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="type" xsi:type="string">vendor_module_mass_invoice</item>
<item name="label" xsi:type="string" translate="true">Invoice</item>
<item name="url" xsi:type="url" path="myroute/order/massInvoice"/>
</item>
</argument>
</action>
</massaction>
</listingToolbar>
</listing>
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="myroute" frontName="myroute">
<module name="Vendor_Module" />
</route>
</router>
</config>
**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\Model\Order\Email\Sender\InvoiceSender;
use Magento\Sales\Model\Order\Invoice;
use Magento\Sales\Model\Order;
class MassInvoice extends \Magento\Sales\Controller\Adminhtml\Order\AbstractMassAction {
/**
* @var InvoiceSender
*/
protected $_invoiceSender;
/**
* @var object
*/
protected $collectionFactory;
public function __construct(
Context $context,
Filter $filter,
CollectionFactory $collectionFactory,
InvoiceSender $invoiceSender
) {
parent::__construct($context, $filter);
$this->collectionFactory = $collectionFactory;
$this->_invoiceSender = $invoiceSender;
}
protected function massAction(AbstractCollection $collection) {
$countInvoice = 0;
foreach ($collection->getItems() as $order) {
// Check if order can be invoiced
if (!$order->canInvoice()) {
$this->messageManager->addError(__('Cannot create invoice for order %1', $order->getIncrementId()));
continue;
}
try {
$invoice = $order->prepareInvoice();
$invoice->getOrder()->setIsInProcess(true);
// Offline only
$invoice->setRequestedCaptureCase(Invoice::CAPTURE_OFFLINE);
// Save & set paid
$invoice->register()->pay();
$invoice->save();
// Uncomment if you also want to send an e-mail
//$this->_invoiceSender->send($invoice);
// Change order status
$order->setState(Order::STATE_PROCESSING)->setStatus(Order::STATE_PROCESSING);
$order->save();
$countInvoice++;
} catch (\Exception $e) {
$this->messageManager->addError(__('Error creating invoice for order %1', $order->getIncrementId()));
}
}
if ($countInvoice) {
$this->messageManager->addSuccess(__('Created invoice for %1 order(s)', $countInvoice));
}
$resultRedirect = $this->resultRedirectFactory->create();
$resultRedirect->setPath($this->getComponentRefererUrl());
return $resultRedirect;
}
}
Be sure to run php bin/magento cache:clean
after making these changes to your module. You should now have an "Invoice" option available in your order grid. This text can be changed in sales_order_grid.xml if you so desire.