Skip to content

Commit

Permalink
magento#16069: Configurable product price is not displayed if all chi…
Browse files Browse the repository at this point in the history
…ldren are out of stock and even if Display Out of Stock Products is set to "yes"

- allow "out of stock" items to be used for price determination of configurable product ONLY in case all child products are "out of stock".
  • Loading branch information
vpodorozh committed Oct 22, 2018
1 parent cbd8d8a commit 80f6a36
Show file tree
Hide file tree
Showing 10 changed files with 385 additions and 15 deletions.
12 changes: 11 additions & 1 deletion app/code/Magento/Catalog/Pricing/Render/FinalPriceBox.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ public function __construct(
*/

This comment has been minimized.

Copy link
@swnsma

swnsma Oct 22, 2018

declare(strict_types=1); is missed.

This comment has been minimized.

Copy link
@vpodorozh

vpodorozh Oct 22, 2018

Author Owner

done

protected function _toHtml()
{
if (!$this->salableResolver->isSalable($this->getSaleableItem())) {
if (!$this->salableResolver->isSalable($this->getSaleableItem()) &&
$this->isApplySalableCheck($this->getSaleableItem())) {
return '';
}

Expand All @@ -86,6 +87,15 @@ protected function _toHtml()
return $this->wrapResult($result);
}

/**
* @param SaleableInterface $salableItem
* @return bool
*/
protected function isApplySalableCheck(SaleableInterface $salableItem)
{
return true;
}

/**
* Check is MSRP applicable for the current product.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

This comment has been minimized.

Copy link
@swnsma

swnsma Oct 22, 2018

declare(strict_types=1); is missed.

This comment has been minimized.

Copy link
@vpodorozh

vpodorozh Oct 22, 2018

Author Owner

done

/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\ConfigurableProduct\Model\ResourceModel\Product;

use Magento\Framework\DB\Select;

/**
* Interface BaseSelectProcessorInterface
* @api
*/
interface BaseSelectProcessorInterface
{
/**
* Product table alias
*/
const PRODUCT_TABLE_ALIAS = 'child';

/**
* @param Select $select
* @param int $productId
* @return Select
*/
public function process(Select $select, $productId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
*/

This comment has been minimized.

Copy link
@swnsma

swnsma Oct 22, 2018

declare(strict_types=1); is missed.

This comment has been minimized.

Copy link
@vpodorozh

vpodorozh Oct 22, 2018

Author Owner

done

namespace Magento\ConfigurableProduct\Model\ResourceModel\Product;

use Magento\Catalog\Model\ResourceModel\Product\BaseSelectProcessorInterface;
use Magento\Catalog\Model\ResourceModel\Product\LinkedProductSelectBuilderInterface;

/**
Expand Down Expand Up @@ -46,7 +45,7 @@ public function build($productId)
$selects = $this->linkedProductSelectBuilder->build($productId);

foreach ($selects as $select) {
$this->baseSelectProcessor->process($select);
$this->baseSelectProcessor->process($select, $productId);
}

return $selects;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
* Copyright © Magento, Inc. All rights reserved.

This comment has been minimized.

Copy link
@swnsma

swnsma Oct 22, 2018

declare(strict_types=1); is missed.

This comment has been minimized.

Copy link
@vpodorozh

vpodorozh Oct 22, 2018

Author Owner

done

* See COPYING.txt for license details.
*/

namespace Magento\ConfigurableProduct\Model\ResourceModel\Product;

use Magento\Framework\DB\Select;
use Magento\Catalog\Model\ResourceModel\Product\BaseSelectProcessorInterface;
use Magento\CatalogInventory\Api\StockConfigurationInterface;
use Magento\CatalogInventory\Model\Stock\Status as StockStatus;
use Magento\CatalogInventory\Model\ResourceModel\Stock\Status as StockStatusResource;
use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\StockStatusInterface as StockStatusConfigurableInterface;

/**
* A Select object processor.
Expand All @@ -28,24 +29,35 @@ class StockStatusBaseSelectProcessor implements BaseSelectProcessorInterface
*/
private $stockStatusResource;

/**
* @var StockStatusConfigurableInterface
*/
private $stockStatusConfigurableResource;

/**
* @param StockConfigurationInterface $stockConfig
* @param StockStatusResource $stockStatusResource
* @param StockStatusConfigurableInterface $stockStatusConfigurableResource
*/
public function __construct(
StockConfigurationInterface $stockConfig,
StockStatusResource $stockStatusResource
) {
StockStatusResource $stockStatusResource,
StockStatusConfigurableInterface $stockStatusConfigurableResource

This comment has been minimized.

Copy link
@swnsma

swnsma Oct 22, 2018

Why do you miss default 'null' value?

This comment has been minimized.

Copy link
@vpodorozh

vpodorozh Oct 22, 2018

Author Owner

done

)

This comment has been minimized.

Copy link
@swnsma

swnsma Oct 22, 2018

Use Magento brackets formatting

This comment has been minimized.

Copy link
@vpodorozh

vpodorozh Oct 22, 2018

Author Owner

done

{
$this->stockConfig = $stockConfig;
$this->stockStatusResource = $stockStatusResource;
$this->stockStatusConfigurableResource = $stockStatusConfigurableResource;
}

/**
* {@inheritdoc}
*/
public function process(Select $select)
public function process(Select $select, $productId)
{
if ($this->stockConfig->isShowOutOfStock()) {
if ($this->stockConfig->isShowOutOfStock() &&
!$this->isAllChildOutOfStock($productId)
) {
$select->joinInner(
['stock' => $this->stockStatusResource->getMainTable()],
sprintf(
Expand All @@ -61,4 +73,14 @@ public function process(Select $select)

return $select;
}

/**
* @param int $productId
* @return bool
* @throws \Exception
*/
protected function isAllChildOutOfStock($productId)
{
return $this->stockStatusConfigurableResource->isAllChildOutOfStock($productId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

This comment has been minimized.

Copy link
@swnsma

swnsma Oct 22, 2018

declare(strict_types=1); is missed.

This comment has been minimized.

Copy link
@vpodorozh

vpodorozh Oct 22, 2018

Author Owner

done

/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable;

use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\App\ResourceConnection;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\CatalogInventory\Api\Data\StockStatusInterface as CatalogInventoryStockStatusInterface;

class StockStatus implements StockStatusInterface
{
/**
* @var ResourceConnection
*/
private $resource;

/**
* @var MetadataPool
*/
private $metadataPool;

/**
* StockStatus constructor.
* @param ResourceConnection $resource
* @param MetadataPool $metadataPool
*/
public function __construct(ResourceConnection $resource, MetadataPool $metadataPool)
{
$this->resource = $resource;
$this->metadataPool = $metadataPool;
}

/**
* @var array
*/
private $allChildOutOfStockInfo = [];

/**
* @inheritdoc
*/
public function isAllChildOutOfStock($productId)
{
if (isset($this->allChildOutOfStockInfo[$productId])) {
return $this->allChildOutOfStockInfo[$productId];
}

$statuses = $this->getAllChildStockInfo($productId);
$isAllChildOutOfStock = true;
foreach ($statuses as $status) {
if ($status == CatalogInventoryStockStatusInterface::STATUS_IN_STOCK) {

This comment has been minimized.

Copy link
@swnsma

swnsma Oct 22, 2018

Can we add strict check?

This comment has been minimized.

Copy link
@vpodorozh

vpodorozh Oct 22, 2018

Author Owner

done

$isAllChildOutOfStock = false;
break;
}
}

$this->allChildOutOfStockInfo[$productId] = $isAllChildOutOfStock;
return $this->allChildOutOfStockInfo[$productId];
}

/**
* @return \Magento\Framework\DB\Adapter\AdapterInterface
*/
protected function getConnection()
{
return $this->resource->getConnection(ResourceConnection::DEFAULT_CONNECTION);

This comment has been minimized.

Copy link
@swnsma

swnsma Oct 22, 2018

Does it really required?
Looks like it already pre-defined in method declaration

This comment has been minimized.

Copy link
@vpodorozh

vpodorozh Oct 22, 2018

Author Owner

removed

}

/**
* @param int $productId
* @return array
* @throws \Exception
*/
protected function getAllChildStockInfo($productId)

This comment has been minimized.

Copy link
@swnsma

swnsma Oct 22, 2018

protected -> private
Check: 2.7

This comment has been minimized.

Copy link
@vpodorozh

vpodorozh Oct 22, 2018

Author Owner

set to private

{
$linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField();
$productTable = $this->resource->getTableName('catalog_product_entity');
$productRelationTable = $this->resource->getTableName('catalog_product_relation');

$select = $this->getConnection()->select()
->from(['parent' => $productTable], '', [])
->joinInner(
['link' => $productRelationTable],
"link.parent_id = parent.$linkField",
['id' => 'child_id']
)->joinInner(
['stock' => $this->resource->getTableName('cataloginventory_stock_status')],
'stock.product_id = link.child_id',
['stock_status']
)->where(sprintf('parent.%s = ?', $linkField), $productId);

return $this->getConnection()->fetchPairs($select);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

This comment has been minimized.

Copy link
@swnsma

swnsma Oct 22, 2018

declare(strict_types=1); is missed.

This comment has been minimized.

Copy link
@vpodorozh

vpodorozh Oct 22, 2018

Author Owner

done

/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable;

/**
* Interface StockStatusInterface
* @api
*/
interface StockStatusInterface
{
/**
* @param int $productId
* @return bool
* @throws \Exception
*/
public function isAllChildOutOfStock($productId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Magento\Framework\Pricing\Render\RendererPool;

This comment has been minimized.

Copy link
@swnsma

swnsma Oct 22, 2018

declare(strict_types=1); is missed.

This comment has been minimized.

Copy link
@vpodorozh

vpodorozh Oct 22, 2018

Author Owner

done

use Magento\Framework\Pricing\SaleableInterface;
use Magento\Framework\View\Element\Template\Context;
use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\StockStatus;

class FinalPriceBox extends \Magento\Catalog\Pricing\Render\FinalPriceBox
{
Expand All @@ -24,6 +25,11 @@ class FinalPriceBox extends \Magento\Catalog\Pricing\Render\FinalPriceBox
*/
private $lowestPriceOptionsProvider;

/**
* @var StockStatus
*/
private $stockStatus;

/**
* @param Context $context
* @param SaleableInterface $saleableItem
Expand All @@ -34,6 +40,7 @@ class FinalPriceBox extends \Magento\Catalog\Pricing\Render\FinalPriceBox
* @param LowestPriceOptionsProviderInterface $lowestPriceOptionsProvider
* @param SalableResolverInterface|null $salableResolver
* @param MinimalPriceCalculatorInterface|null $minimalPriceCalculator
* @param StockStatus $stockStatus
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function __construct(
Expand All @@ -45,7 +52,8 @@ public function __construct(
array $data = [],
LowestPriceOptionsProviderInterface $lowestPriceOptionsProvider = null,
SalableResolverInterface $salableResolver = null,
MinimalPriceCalculatorInterface $minimalPriceCalculator = null
MinimalPriceCalculatorInterface $minimalPriceCalculator = null,
StockStatus $stockStatus = null
) {
parent::__construct(
$context,
Expand All @@ -58,6 +66,7 @@ public function __construct(
);
$this->lowestPriceOptionsProvider = $lowestPriceOptionsProvider ?:
ObjectManager::getInstance()->get(LowestPriceOptionsProviderInterface::class);
$this->stockStatus = $stockStatus ?: ObjectManager::getInstance()->get(StockStatus::class);
}

/**
Expand All @@ -77,4 +86,12 @@ public function hasSpecialPrice()
}
return false;
}

/**
* @inheritdoc
*/
protected function isApplySalableCheck(SaleableInterface $salableItem)
{
return !$this->stockStatus->isAllChildOutOfStock($salableItem->getId());
}
}
Loading

1 comment on commit 80f6a36

@vpodorozh
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed and fixed

Please sign in to comment.