diff --git a/CHANGELOG.md b/CHANGELOG.md index c511d186c8..607c2e2a8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,11 @@ ## Unreleased +- Fixed a bug where soft-deleted variants were not being restored when the product was restored. ([#3815](https://github.com/craftcms/commerce/issues/3815)) +- Fixed a bug where inventory movements did not update the purchasable’s per-store cached stock total. +- Fixed a bug where transfers that contained deleted inventory items could not be updated. +- Added `craft\commerce\collections\InventoryMovementCollection::getPurchasables()`. - Added `craft\commerce\base\Gateway::transactionSupportsRefund()`. -- Fixed a bug where soft-deleted variants were not being restored when the product was restored. ([#3815](https://github.com/craftcms/commerce/issues/3815)) - Fixed a bug where the `commerce/cart/update-cart` action could return unnecessary validation errors. ([3873](https://github.com/craftcms/commerce/issues/3873)) ## 5.3.1 - 2025-02-03 diff --git a/src/collections/InventoryMovementCollection.php b/src/collections/InventoryMovementCollection.php index 408df62f0f..527406febe 100644 --- a/src/collections/InventoryMovementCollection.php +++ b/src/collections/InventoryMovementCollection.php @@ -8,6 +8,7 @@ namespace craft\commerce\collections; use craft\commerce\base\InventoryMovement; +use craft\commerce\base\InventoryMovementInterface; use Illuminate\Support\Collection; /** @@ -22,4 +23,14 @@ */ class InventoryMovementCollection extends Collection { + /** + * @return array + * @since 5.3.2 + */ + public function getPurchasables(): array + { + return $this->map(function(InventoryMovementInterface $updateInventoryLevel) { + return $updateInventoryLevel->getInventoryItem()->getPurchasable(); + })->all(); + } } diff --git a/src/controllers/TransfersController.php b/src/controllers/TransfersController.php index 5a167c5c9e..1bcfc06c1c 100644 --- a/src/controllers/TransfersController.php +++ b/src/controllers/TransfersController.php @@ -177,7 +177,7 @@ public function actionReceiveTransfer(): Response $transferDetails = $transfer->getDetails(); foreach ($transferDetails as $detail) { - if ($acceptedAmount = $details[$detail->uid]['accept']) { + if ($acceptedAmount = $details[$detail->uid]['accept'] ?? null) { // Update the total accepted $detail->quantityAccepted += $acceptedAmount; @@ -193,7 +193,7 @@ public function actionReceiveTransfer(): Response $inventoryMovementCollection->push($inventoryAcceptedMovement); } - if ($rejectedAmount = $details[$detail->uid]['reject']) { + if ($rejectedAmount = $details[$detail->uid]['reject'] ?? null) { // Update the total rejected $detail->quantityRejected += $rejectedAmount; @@ -255,6 +255,7 @@ public function actionReceiveTransferScreen(): Response $tableRows = ''; foreach ($transfer->getDetails() as $detail) { + $deleted = $detail->inventoryItemId == null; $key = $detail->uid; $purchasable = $detail->getInventoryItem()?->getPurchasable(CraftCp::requestedSite()->id); $label = $purchasable ? CraftCp::elementChipHtml($purchasable) : $detail->inventoryItemDescription; @@ -262,11 +263,19 @@ public function actionReceiveTransferScreen(): Response $tableRows .= Html::tag('td', $label); $tableRows .= Html::tag('td', (string)$detail->quantityAccepted, ['class' => 'rightalign']); $tableRows .= Html::tag('td', - Html::input('number', 'details[' . $key . '][accept]', '', ['class' => 'text fullwidth']) + Html::input('number', 'details[' . $key . '][accept]', '', [ + 'class' => 'text fullwidth', + 'disabled' => $deleted, + 'placeholder' => $deleted ? Craft::t('app', '“{name}” deleted.', ['name' => $detail->inventoryItemDescription]) : '', + ]) ); $tableRows .= Html::tag('td', (string)$detail->quantityRejected, ['class' => 'rightalign']); $tableRows .= Html::tag('td', - Html::input('number', 'details[' . $key . '][reject]', '', ['class' => 'text fullwidth']) + Html::input('number', 'details[' . $key . '][reject]', '', [ + 'class' => 'text fullwidth', + 'disabled' => $deleted, + 'placeholder' => $deleted ? Craft::t('app', '“{name}” deleted.', ['name' => $detail->inventoryItemDescription]) : '', + ]) ); } diff --git a/src/services/Inventory.php b/src/services/Inventory.php index dea31979d0..c0e737b448 100644 --- a/src/services/Inventory.php +++ b/src/services/Inventory.php @@ -318,6 +318,7 @@ public function executeUpdateInventoryLevels(UpdateInventoryLevelCollection $upd $transaction->commit(); + // TODO: Potentially move this to a job in the queue // Update all purchasables stock $purchasables = $updateInventoryLevels->getPurchasables(); if ($purchasables) { @@ -381,7 +382,7 @@ public function updatePurchasableInventoryLevel(Purchasable $purchasable, int $q $this->updateInventoryLevel($purchasable->inventoryItemId, $quantity, $updateInventoryLevelAttributes); // Clear the stock cache for the class instance - unset($purchasable->stock); + unset($purchasable->stock); // set _stock to null } /** @@ -530,8 +531,14 @@ public function executeInventoryMovements(InventoryMovementCollection $inventory $transaction->commit(); - // TODO: Update stock value on purchasable stores - // Craft::$app->getElements()->invalidateCachesForElement($this); + // TODO: Potentially move this to a job in the queue + foreach ($inventoryMovements as $inventoryMovement) { + // Update all purchasables stock + $purchasable = $inventoryMovement->getInventoryItem()->getPurchasable(); + if ($purchasable) { + Plugin::getInstance()->getPurchasables()->updateStoreStockCache($purchasable, true); + } + } return true; } catch (\Exception $e) {