diff --git a/vendor/magento/module-sales/Model/InvoiceOrder.php b/vendor/magento/module-sales/Model/InvoiceOrder.php index 875a6c27c61a7..74f788460afc2 100644 --- a/vendor/magento/module-sales/Model/InvoiceOrder.php +++ b/vendor/magento/module-sales/Model/InvoiceOrder.php @@ -6,6 +6,7 @@ namespace Magento\Sales\Model; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ResourceConnection; use Magento\Sales\Api\Data\InvoiceCommentCreationInterface; use Magento\Sales\Api\Data\InvoiceCreationArgumentsInterface; @@ -21,7 +22,8 @@ use Psr\Log\LoggerInterface; /** - * Class InvoiceOrder + * Creates invoice for an order + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class InvoiceOrder implements InvoiceOrderInterface @@ -76,6 +78,11 @@ class InvoiceOrder implements InvoiceOrderInterface */ private $logger; + /** + * @var OrderMutexInterface + */ + private $orderMutex; + /** * InvoiceOrder constructor. * @param ResourceConnection $resourceConnection @@ -88,6 +95,7 @@ class InvoiceOrder implements InvoiceOrderInterface * @param InvoiceOrderValidator $invoiceOrderValidator * @param NotifierInterface $notifierInterface * @param LoggerInterface $logger + * @param OrderMutex|null $orderMutex * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -100,7 +108,8 @@ public function __construct( InvoiceRepository $invoiceRepository, InvoiceOrderValidator $invoiceOrderValidator, NotifierInterface $notifierInterface, - LoggerInterface $logger + LoggerInterface $logger, + ?OrderMutexInterface $orderMutex = null ) { $this->resourceConnection = $resourceConnection; $this->orderRepository = $orderRepository; @@ -112,9 +121,12 @@ public function __construct( $this->invoiceOrderValidator = $invoiceOrderValidator; $this->notifierInterface = $notifierInterface; $this->logger = $logger; + $this->orderMutex = $orderMutex ?: ObjectManager::getInstance()->get(OrderMutexInterface::class); } /** + * Creates invoice for provided order ID + * * @param int $orderId * @param bool $capture * @param array $items @@ -138,7 +150,48 @@ public function execute( InvoiceCommentCreationInterface $comment = null, InvoiceCreationArgumentsInterface $arguments = null ) { - $connection = $this->resourceConnection->getConnection('sales'); + return $this->orderMutex->execute( + (int) $orderId, + \Closure::fromCallable([$this, 'createInvoice']), + [ + $orderId, + $capture, + $items, + $notify, + $appendComment, + $comment, + $arguments + ] + ); + } + + /** + * Creates invoice for provided order ID + * + * @param int $orderId + * @param bool $capture + * @param array $items + * @param bool $notify + * @param bool $appendComment + * @param \Magento\Sales\Api\Data\InvoiceCommentCreationInterface|null $comment + * @param \Magento\Sales\Api\Data\InvoiceCreationArgumentsInterface|null $arguments + * @return int + * @throws \Magento\Sales\Api\Exception\DocumentValidationExceptionInterface + * @throws \Magento\Sales\Api\Exception\CouldNotInvoiceExceptionInterface + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \DomainException + * @SuppressWarnings(PHPMD.UnusedPrivateMethod) + */ + private function createInvoice( + $orderId, + $capture = false, + array $items = [], + $notify = false, + $appendComment = false, + InvoiceCommentCreationInterface $comment = null, + InvoiceCreationArgumentsInterface $arguments = null + ) { $order = $this->orderRepository->get($orderId); $invoice = $this->invoiceDocumentFactory->create( $order, @@ -162,7 +215,6 @@ public function execute( __("Invoice Document Validation Error(s):\n" . implode("\n", $errorMessages->getMessages())) ); } - $connection->beginTransaction(); try { $order = $this->paymentAdapter->pay($order, $invoice, $capture); $order->setState( @@ -172,10 +224,8 @@ public function execute( $invoice->setState(\Magento\Sales\Model\Order\Invoice::STATE_PAID); $this->invoiceRepository->save($invoice); $this->orderRepository->save($order); - $connection->commit(); } catch (\Exception $e) { $this->logger->critical($e); - $connection->rollBack(); throw new \Magento\Sales\Exception\CouldNotInvoiceException( __('Could not save an invoice, see error log for details') ); diff --git a/vendor/magento/module-sales/Model/OrderMutex.php b/vendor/magento/module-sales/Model/OrderMutex.php new file mode 100644 index 0000000000000..44446cc78db2b --- /dev/null +++ b/vendor/magento/module-sales/Model/OrderMutex.php @@ -0,0 +1,53 @@ +resourceConnection = $resourceConnection; + } + + /** + * @inheritdoc + */ + public function execute(int $orderId, callable $callable, array $args = []) + { + $connection = $this->resourceConnection->getConnection('sales'); + $connection->beginTransaction(); + $query = $connection->select() + ->from($this->resourceConnection->getTableName('sales_order'), 'entity_id') + ->where('entity_id = ?', $orderId) + ->forUpdate(true); + $connection->query($query); + + try { + $result = $callable(...$args); + $connection->commit(); + return $result; + } catch (\Throwable $e) { + $connection->rollBack(); + throw $e; + } + } +} diff --git a/vendor/magento/module-sales/Model/OrderMutexInterface.php b/vendor/magento/module-sales/Model/OrderMutexInterface.php new file mode 100644 index 0000000000000..23bb32fd370d8 --- /dev/null +++ b/vendor/magento/module-sales/Model/OrderMutexInterface.php @@ -0,0 +1,24 @@ + +