Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TASK] Listen to ProcessOrderCheckStockEvent #177

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions Classes/EventListener/Order/Stock/CheckStock.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?php

declare(strict_types=1);

namespace Extcode\CartProducts\EventListener\Order\Stock;

/*
* This file is part of the package extcode/cart-products.
*
* For the full copyright and license information, please read the
* LICENSE file that was distributed with this source code.
*/

use Extcode\Cart\Domain\Model\Cart\Product as CartProduct;
use Extcode\Cart\Event\ProcessOrderCheckStockEvent;
use Extcode\CartProducts\Domain\Model\Product\Product;
use Extcode\CartProducts\Domain\Repository\Product\ProductRepository;
use TYPO3\CMS\Core\Messaging\FlashMessage;
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;

class CheckStock
{
private ProcessOrderCheckStockEvent $event;

public function __construct(
protected readonly ProductRepository $productRepository
) {
$querySettings = $this->productRepository->createQuery()->getQuerySettings();
$querySettings->setRespectStoragePage(false);
$this->productRepository->setDefaultQuerySettings($querySettings);
}

public function __invoke(ProcessOrderCheckStockEvent $event): void
{
$this->event = $event;
$cart = $event->getCart();
$cartProducts = $cart->getProducts();

/** @var CartProduct $cartProduct */
foreach ($cartProducts as $cartProduct) {
if ($cartProduct->getProductType() !== 'CartProducts') {
continue;
}

$warehouseProduct = $this->getWarehouseProduct($cartProduct->getProductId());

if (!$warehouseProduct instanceof Product || !$warehouseProduct->isHandleStock()) {
continue;
}

$quantityToSell = $cartProduct->getQuantity();

if ($this->stockHandlingNotInVariant($warehouseProduct, $quantityToSell)) {
continue;
}

$this->stockHandlingInVariant($warehouseProduct, $cartProduct, $quantityToSell);
}
}

private function getWarehouseProduct(int $productId): null|DomainObjectInterface
{

return $this->productRepository->findByIdentifier($productId);
}

private function stockHandlingNotInVariant(Product $product, int $quantityToSell): bool
{
if (!$product->isHandleStockInVariants()) {
$quantityInStock = $product->getStock();

if ($quantityToSell > $quantityInStock) {
$this->falseAvailability($this->event, $product->getTitle(), $product->getSku(), $quantityInStock);
}
return true;
}

return false;
}

private function falseAvailability(
ProcessOrderCheckStockEvent $event,
string $title,
string $sku,
int $quantityInStock
): void {
$event->setNotEveryProductAvailable();
$event->addInsufficientStockMessage(
GeneralUtility::makeInstance(
FlashMessage::class,
LocalizationUtility::translate(
'tx_cart.error.stock_handling.order',
'cart',
[$title, $sku, $quantityInStock]
),
'',
ContextualFeedbackSeverity::ERROR
)
);
}

private function stockHandlingInVariant(
Product $productInWarehouse,
CartProduct $cartProduct,
int $quantityToSell
): void {
foreach ($productInWarehouse->getBeVariants() as $beVariantWarehouse) {
foreach ($cartProduct->getBeVariants() as $beVariantCart) {
if ($beVariantCart->getSku() !== $beVariantWarehouse->getSku()) {
continue;
}

$quantityInStock = $beVariantWarehouse->getStock();

if ($quantityToSell > $quantityInStock) {
$this->falseAvailability(
$this->event,
$productInWarehouse->getTitle(),
$beVariantWarehouse->getSku(),
$quantityInStock
);
}
}
}
}
}
7 changes: 7 additions & 0 deletions Configuration/Services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ services:
identifier: 'cart-products--check-product-availability'
event: Extcode\Cart\Event\CheckProductAvailabilityEvent

Extcode\CartProducts\EventListener\Order\Stock\CheckStock:
tags:
- name: event.listener
identifier: 'cart-products--order--stock-check-stock'
event: Extcode\Cart\Event\ProcessOrderCheckStockEvent


Extcode\CartProducts\Reaction\UpdateStockReaction:
tags: ['reactions.reaction']
public: true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.. include:: ../../Includes.txt

======================================================
Breaking: #138 - Listen to ProcessOrderCheckStockEvent
======================================================

See :Issue 138 <https://github.com/extcode/cart_products/issues/138>`

Description
===========

`EXT:cart` v9 has an extended :php:`Extcode\Cart\Domain\Model\Cart\Cart\ProcessOrderCheckStockEvent`.
This allows product extensions as `EXT:cart_product` to set a flag if any
product of an order is not available in sufficient amount (means: less articles
in stock than what a customer wants to order).

The new Event Listener :php:`Extcode\CartProducts\EventListener\Order\Stock\CheckStock`
checks whether the stock of any article is lower than what a customer wants to
order. This situation can happen when customer B ordered the same product
after customer A put the article into the cart. Customer A will get in this case
a message that the article is no longer available in the desired amount and the
order can not be finished until customer A adapts the amount.

This prevents a TYPO3 Exception "[..] Out of range value for column 'stock'
[...]".

Impact
======

Negative effects are not expected. This feature works out of the box when
stock handling is activated. As long as flash messages are shown in the order
form it's not necessary to adapt anything.

It is of course possible to override the displayed message which has the key
`tx_cart.error.stock_handling.order` (in EXT:cart).

.. index:: Backend
Loading