Skip to content

Commit

Permalink
fix #2: stock decrease on order cancel
Browse files Browse the repository at this point in the history
  • Loading branch information
ericges committed Dec 3, 2024
1 parent 9e90f99 commit d0388ed
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 45 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

All notable changes to this project will be documented in this file.

## [0.1.3] - 2024-12-03
- Fixed: [#2](https://github.com/heimrichhannot/contao-isotope-stock-bundle/issues/2) stock decrease on order cancel

## [0.1.2] - 2024-09-16
- Fixed: exception in migration if fields not exist

## [0.1.1] - 2023-12-18
- Fixed: exception in stock calculation on post checkout

## [0.1.0] - 2023-11-17
Initial version.
Initial version.
133 changes: 90 additions & 43 deletions src/EventListener/Isotope/PreOrderStatusUpdateListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

namespace HeimrichHannot\IsotopeStockBundle\EventListener\Isotope;

use Doctrine\DBAL\Connection;
use HeimrichHannot\IsotopeStockBundle\ProductAttribute\MaxOrderSizeAttribute;
use HeimrichHannot\IsotopeStockBundle\ProductAttribute\StockAttribute;
use HeimrichHannot\UtilsBundle\Util\Utils;
use Isotope\Model\OrderStatus;
use Isotope\Model\ProductCollection\Order;
use Isotope\Model\ProductCollectionItem;
use Isotope\ServiceAnnotation\IsotopeHook;

/**
Expand All @@ -15,15 +17,16 @@
class PreOrderStatusUpdateListener
{
public function __construct(
private Utils $utils,
private StockAttribute $stockAttribute,
private MaxOrderSizeAttribute $maxOrderSizeAttribute,
private readonly Connection $connection,
private readonly Utils $utils,
private readonly StockAttribute $stockAttribute,
private readonly MaxOrderSizeAttribute $maxOrderSizeAttribute,
)
{
}

/**
* @return bool Cancel the order status transition
* @return bool Cancel the order status transition if the stock increase/decrease fails
*/
public function __invoke(Order $order, OrderStatus $newsStatus, array $updates): bool
{
Expand All @@ -32,52 +35,96 @@ public function __invoke(Order $order, OrderStatus $newsStatus, array $updates):
return false;
}

$oldStatus = OrderStatus::findByPk($order->order_status);
if (!$oldStatus) {
if (!$oldStatus = OrderStatus::findByPk($order->order_status)) {
return false;
}

// e.g. new -> cancelled => increase the stock based on the order item's setQuantity-values (no validation required, of course)
if (!$oldStatus->stock_increaseStock && $newsStatus->stock_increaseStock) {
foreach ($order->getItems() as $item) {
$product = $item->getProduct();
if (!$product) {
continue;
}

if ($this->stockAttribute->isActive($product)) {
$product->stock = (int)$product->stock + (int)$item->quantity;
$product->save();
}

if ($this->maxOrderSizeAttribute->isActive($product)) {
if (!$this->maxOrderSizeAttribute->validateQuantity($product, $item->quantity)) {
return true;
}
}

}
if ((bool) $oldStatus->stock_increaseStock === (bool) $newsStatus->stock_increaseStock)
// No stock action change? Nothing to do here.
{
return false;
}
// e.g. cancelled -> new => decrease the stock after validation
elseif ($oldStatus->stock_increaseStock && !$newsStatus->stock_increaseStock) {
foreach ($order->getItems() as $item) {
if (!$product = $item->getProduct()) {
continue;
}


if (null !== ($product = $item->getProduct())) {
if (!$this->stockAttribute->validateQuantity($product, $item->quantity)) {
// if the validation breaks for only one product collection item -> cancel the order status transition
return true;
}
}

$product->stock = (int)$product->stock - (int)$item->quantity;
$product->save();

// Determine the appropriate callback based on stock change
// e.g. new -> cancelled: increase the stock based on the ordered item's quantity
// e.g. cancelled -> new: decrease the stock
$callback = !$oldStatus->stock_increaseStock && $newsStatus->stock_increaseStock
? 'increaseStock'
: 'decreaseStock';

foreach ($order->getItems() as $item)
{
if (!$this->$callback($item))
{
return true;
}
}

return false;
}

/**
* @param ProductCollectionItem $item
* @return bool False if the stock increase failed
* @noinspection PhpUnused
*/
protected function increaseStock(ProductCollectionItem $item): bool
{
if (!$product = $item->getProduct()) {
return true;
}

if ($this->stockAttribute->isActive($product))
{
$newStock = (int)$product->stock + (int)$item->quantity;

$this->connection
->prepare("UPDATE `{$product::getTable()}` SET stock = ? WHERE id = ?")
->executeStatement([$newStock, $product->id]);

$product->stock = $newStock;
}

if ($this->maxOrderSizeAttribute->isActive($product)
&& !$this->maxOrderSizeAttribute->validateQuantity($product, $item->quantity))
// validation needed after stock increase
{
return false;
}

return true;
}

/**
* @param ProductCollectionItem $item
* @return bool False if the stock decrease failed
* @noinspection PhpUnused
*/
protected function decreaseStock(ProductCollectionItem $item): bool
{
if (!$product = $item->getProduct()) {
return true;
}

if (!$this->stockAttribute->validateQuantity($product, $item->quantity))
// validation needed before stock decrease
// if the validation breaks, cancel the order status transition
{
return false;
}

$newStock = (int)$product->stock - (int)$item->quantity;

if ($newStock < 0) {
return false;
}

$this->connection
->prepare("UPDATE `{$product::getTable()}` SET stock = ? WHERE id = ?")
->executeStatement([$newStock, $product->id]);

$product->stock = $newStock;

return true;
}
}
2 changes: 1 addition & 1 deletion src/ProductAttribute/StockAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function validateQuantity(IsotopeProduct $product, int $quantity): bool
return true;
}

if (0 === (int)$product->stock) {
if ((int)$product->stock < 1) {
$this->addErrorMessage($this->translator->trans('MSC.stockEmpty', [$product->getName()], 'contao_default'));
return false;
}
Expand Down

0 comments on commit d0388ed

Please sign in to comment.