4

I'm testing on Magento 2.2.3 and I've created an observer for the event sales_order_save_after which I'm using to automatically create an invoice.

Here is the current error that I'm receiving after placing an order:

Order saving error: Rolled back transaction has not been completed correctly.

And my MyCompany/MyModule/Observer/SalesOrderSaveAfter.php

<?php
namespace MyCompany\MyModule\Observer;

use Magento\Framework\Event\ObserverInterface;

class SalesOrderSaveAfter implements ObserverInterface
{
    protected $_invoiceService;
    protected $_transactionFactory;

    public function __construct(
      \Magento\Sales\Model\Service\InvoiceService $invoiceService,
      \Magento\Framework\DB\TransactionFactory $transactionFactory
    ) {
       $this->_invoiceService = $invoiceService;
       $this->_transactionFactory = $transactionFactory;
    }

    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        $order = $observer->getEvent()->getOrder();

        try {
            if(!$order->canInvoice()) {
                return null;
            }
            if(!$order->getState() == 'new') {
                return null;
            }

            $invoice = $this->_invoiceService->prepareInvoice($order);
            $invoice->setRequestedCaptureCase(\Magento\Sales\Model\Order\Invoice::CAPTURE_ONLINE);
            $invoice->register();

            $transaction = $this->_transactionFactory->create()
              ->addObject($invoice)
              ->addObject($invoice->getOrder());

            $transaction->save();

        } catch (\Exception $e) {
            $order->addStatusHistoryComment('Exception message: '.$e->getMessage(), false);
            $order->save();
            return null;
        }
    }
}

If I remove the transaction portion of the code, eg:

$transaction = $this->_transactionFactory->create()
    ->addObject($invoice)
    ->addObject($invoice->getOrder());

    $transaction->save();

then the order will pass through with the products marked as invoiced, but no invoice is actually created or saved to the order.

Any ideas what I could be missing?

7
  • 3
    The biggest problem is that your order hadn't yet been committed to the database and so your transaction results in a foreign key constraint error. (i.e. the sales_invoice table has an order_id column but your order hasn't yet been committed) Does this need to fired after all transactions or just those on the frontend? If the latter, try using the checkout_onepage_controller_success_action Mar 13, 2018 at 4:20
  • 2
    @Aj47, Yes Phil Birnie is right. You should use checkout_onepage_controller_success_action action to create invoice Mar 13, 2018 at 5:25
  • @PhilBirnie ah that makes sense then, I actually need this to fire from orders placed from the admin as well. I've also tried sales_order_place_after but ran into the same issue. Is there an event that would work for admin orders?
    – AJK
    Mar 13, 2018 at 13:11
  • 2
    looks like checkout_submit_all_after did the trick and I'll just move events.xml to adminhtml folder so it only fires from admin. Is that the best event to use for this application?
    – AJK
    Mar 13, 2018 at 14:19
  • I want to create an invoice and send mail when order shipped. Have you any idea about this? Feb 13, 2019 at 9:05

2 Answers 2

6

As pointed out, the answer to this is that I was using the wrong event. With the event sales_order_save_after the order hasn't been committed to the Database yet.

I changed my event to fire on checkout_submit_all_after and my observer is now working.

9
  • Do you understand why after the sales_order_save_after event the order hasn't been committed to the database?
    – LucScu
    Jun 6, 2018 at 8:05
  • @LucaS Unfortunately, I'm not 100% sure why this is the case -- I know in Magento 1.8.x+ I could use sales_order_save_after event to fire off an automatic invoice. But not sure the difference in M2
    – AJK
    Jun 6, 2018 at 20:00
  • i tried this in magento2.3 but it not work Feb 19, 2019 at 8:45
  • @JigsParmar , just tested on our 2.3 store and seems to still be working. You may have something else going on. Feel free to post a separate question and I can take a look!
    – AJK
    Feb 19, 2019 at 14:23
  • its working now , i trying to generate shipping programatically thats the issue Feb 20, 2019 at 10:54
4

Automatically Create Invoice after place the order from Order Observer using event checkout_submit_all_after. Follow below file path to create custom module and makes auto invoice after place order.

File path: magento/app/code/Vendor/AutoInvoice/registration.php

<?php

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Vendor_AutoInvoice',
    __DIR__
);

File path: magento/app/code/Vendor/AutoInvoice/etc/module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Vendor_AutoInvoice" setup_version="2.1.0" />
</config>

File path: magento/app/code/Vendor/AutoInvoice/etc/events.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="checkout_submit_all_after">
        <observer name="vendor_checkout_submit_all_after" instance="Vendor\AutoInvoice\Observer\OrderObserver" />
    </event>
</config>

File path: magento/app/code/Vendor/AutoInvoice/Observer/OrderObserver.php

<?php
namespace Vendor\AutoInvoice\Observer;

use Magento\Framework\Event\ObserverInterface;

class OrderObserver implements ObserverInterface
{

    /**
     * @var \Magento\Sales\Model\ResourceModel\Order\Invoice\CollectionFactory
     */
    protected $_invoiceCollectionFactory;

    /**
     * @var \Magento\Sales\Api\InvoiceRepositoryInterface
     */
    protected $_invoiceRepository;

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

    /**
     * @var \Magento\Framework\DB\TransactionFactory
     */
    protected $_transactionFactory;

    /**
    * @var \Magento\Sales\Api\OrderRepositoryInterface
    */
    protected $_orderRepository;

    /**
    * @param \Magento\Sales\Model\ResourceModel\Order\Invoice\CollectionFactory $invoiceCollectionFactory
    * @param \Magento\Sales\Model\Service\InvoiceService $invoiceService
    * @param \Magento\Framework\DB\TransactionFactory $transactionFactory
    * @param \Magento\Sales\Api\InvoiceRepositoryInterface $invoiceRepository
    * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository
    */
    public function __construct(
        \Magento\Sales\Model\ResourceModel\Order\Invoice\CollectionFactory $invoiceCollectionFactory,
        \Magento\Sales\Model\Service\InvoiceService $invoiceService,
        \Magento\Framework\DB\TransactionFactory $transactionFactory,
        \Magento\Sales\Api\InvoiceRepositoryInterface $invoiceRepository,
        \Magento\Sales\Api\OrderRepositoryInterface $orderRepository
        ) {
          $this->_invoiceCollectionFactory = $invoiceCollectionFactory;
          $this->_invoiceService = $invoiceService;
          $this->_transactionFactory = $transactionFactory;
          $this->_invoiceRepository = $invoiceRepository;
          $this->_orderRepository = $orderRepository;
    }




    public function execute(\Magento\Framework\Event\Observer $observer)
    {   
        $orderId = $observer->getEvent()->getOrder()->getId();
        $this->createInvoice($orderId);      

    }

    protected function createInvoice($orderId)
    {
        try 
        {
            $order = $this->_orderRepository->get($orderId);
            if ($order)
            {
                $invoices = $this->_invoiceCollectionFactory->create()
                  ->addAttributeToFilter('order_id', array('eq' => $order->getId()));

                $invoices->getSelect()->limit(1);

                if ((int)$invoices->count() !== 0) {
                  $invoices = $invoices->getFirstItem();
                  $invoice = $this->_invoiceRepository->get($invoices->getId());
                  return $invoice;
                }

                if(!$order->canInvoice()) {
                    return null;
                }

                $invoice = $this->_invoiceService->prepareInvoice($order);
                $invoice->setRequestedCaptureCase(\Magento\Sales\Model\Order\Invoice::CAPTURE_ONLINE);
                $invoice->register();
                $invoice->getOrder()->setCustomerNoteNotify(false);
                $invoice->getOrder()->setIsInProcess(true);
                $order->addStatusHistoryComment(__('Automatically INVOICED'), false);
                $transactionSave = $this->_transactionFactory->create()->addObject($invoice)->addObject($invoice->getOrder());
                $transactionSave->save();

                return $invoice;
            }
        } catch (\Exception $e) {
            throw new \Magento\Framework\Exception\LocalizedException(
                __($e->getMessage())
            );
        }
    }
}
1
  • we have use same event and give error on place order {"message":"We can't save the address:\n\"Last name\" is required. Enter and try again.\n\"First Name\" is required. Enter and try again.","trace":"#0 Apr 14, 2020 at 8:00

Your Answer

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

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