I want to show the price of product, that is "out of stock". Currently it is not shown price for the products in my magento 2.1.3 installation.
How to make visible it?
Magento Stack Exchange is a question and answer site for users of the Magento e-Commerce platform. It only takes a minute to sign up.
Sign up to join this communityI want to show the price of product, that is "out of stock". Currently it is not shown price for the products in my magento 2.1.3 installation.
How to make visible it?
You need to modify some logic for that. So create a new module and add following code.
Vendor/Module/etc/frontend/di.xml
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\Catalog\Pricing\Render\FinalPriceBox" type="Vendor\Module\Pricing\Render\FinalPriceBox" /> </config>
Vendor/Module/Pricing/Render/FinalPriceBox.php
namespace Vendor\Module\Pricing\Render; use Magento\Msrp\Pricing\Price\MsrpPrice; use Magento\Framework\Pricing\Render\PriceBox as BasePriceBox; class FinalPriceBox extends \Magento\Catalog\Pricing\Render\FinalPriceBox { protected function _toHtml() { $result = parent::_toHtml(); if(!$result) { $result = BasePriceBox::_toHtml(); try { /** @var MsrpPrice $msrpPriceType */ $msrpPriceType = $this->getSaleableItem()->getPriceInfo()->getPrice('msrp_price'); } catch (\InvalidArgumentException $e) { $this->_logger->critical($e); return $this->wrapResult($result); } //Renders MSRP in case it is enabled $product = $this->getSaleableItem(); if ($msrpPriceType->canApplyMsrp($product) && $msrpPriceType->isMinimalPriceLessMsrp($product)) { /** @var BasePriceBox $msrpBlock */ $msrpBlock = $this->rendererPool->createPriceRender( MsrpPrice::PRICE_CODE, $this->getSaleableItem(), [ 'real_price_html' => $result, 'zone' => $this->getZone(), ] ); $result = $msrpBlock->toHtml(); } return $this->wrapResult($result); } return $result; } }
This one adds to Sohel Rana's answer. Tested on 2.3.3 for configurable products
For any of the product out of stock:
In your di.xml
<preference for="Magento\ConfigurableProduct\Pricing\Render\FinalPriceBox" type="<Vendor>\<Module>\Pricing\Render\FinalPriceBox" />
And in \\Pricing\Render\FinalPriceBox.php
namespace <Vendor>\<Module>\Pricing\Render;
use Magento\Framework\Pricing\Render\PriceBox as BasePriceBox;
class FinalPriceBox extends \Magento\ConfigurableProduct\Pricing\Render\FinalPriceBox
{
protected function _toHtml()
{
$result = BasePriceBox::_toHtml();
//Renders MSRP in case it is enabled
if ($this->isMsrpPriceApplicable()) {
/** @var BasePriceBox $msrpBlock */
$msrpBlock = $this->rendererPool->createPriceRender(
MsrpPrice::PRICE_CODE,
$this->getSaleableItem(),
[
'real_price_html' => $result,
'zone' => $this->getZone(),
]
);
$result = $msrpBlock->toHtml();
}
return $this->wrapResult($result);
}
}
For all sub-products out of stock:
In your di.xml
<type name="Magento\ConfigurableProduct\Model\ResourceModel\Product\LinkedProductSelectBuilder">
<arguments>
<argument name="baseSelectProcessor" xsi:type="object"><Vendor>\<Module>\Model\ConfigurableProduct\ResourceModel\Product\StockStatusBaseSelectProcessor</argument>
</arguments>
</type>
And remove stock select
namespace <Vendor>\<Module>\Model\ConfigurableProduct\ResourceModel\Product;
use Magento\Framework\DB\Select;
class StockStatusBaseSelectProcessor extends \Magento\ConfigurableProduct\Model\ResourceModel\Product\StockStatusBaseSelectProcessor
{
public function process(Select $select)
{
return $select;
}
}
catalog_product_index_price table
which still shows price = 0
for configurable products with out of stock child products. Any idea how to deal with that issue as well?
I just created a small module to show the price of out-of-stock
items based on @Cotton 's answer.
Feel free to fork: https://github.com/nordcomputer/magento2-showoutofstockprice
Composer install should work if you add the repo to your dependencies. Be aware, that I set the required PHP version to ^7.1
- you may need to reconfigure that in the composer.json file if you use another version.
NOPE. 3rd try with different solutions and this also does absolutely nothing. Out of stock items have no price. Any other thoughts on a working solution for Magento 2.2.6
Create file FinalPriceBox.php in app/code/Vendor/Module/Pricing/Render folder with the following code.
<?php
namespace Vendor\Module\Pricing\Render;
use Magento\Catalog\Pricing\Price;
use Magento\Framework\Pricing\Render\PriceBox as BasePriceBox;
use Magento\Msrp\Pricing\Price\MsrpPrice;
use Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolverInterface;
use Magento\Framework\View\Element\Template\Context;
use Magento\Framework\Pricing\SaleableInterface;
use Magento\Framework\Pricing\Price\PriceInterface;
use Magento\Framework\Pricing\Render\RendererPool;
use Magento\Framework\App\ObjectManager;
use Magento\Catalog\Pricing\Price\MinimalPriceCalculatorInterface;
use Magento\Catalog\Pricing\Price\RegularPrice;
use Magento\ConfigurableProduct\Pricing\Price\ConfigurableOptionsProviderInterface;
use Magento\ConfigurableProduct\Pricing\Price\LowestPriceOptionsProviderInterface;
use Magento\Catalog\Pricing\Price\FinalPrice;
/**
* Class for final_price rendering
*
* @method bool getUseLinkForAsLowAs()
* @method bool getDisplayMinimalPrice()
*/
class FinalPriceBox extends BasePriceBox
{
/**
* @var LowestPriceOptionsProviderInterface
*/
private $lowestPriceOptionsProvider;
/**
* @var MinimalPriceCalculatorInterface
*/
private $minimalPriceCalculator;
/**
* @var SalableResolverInterface
*/
private $salableResolver;
/**
* FinalPriceBox constructor
*
* @param Context $context
* @param SaleableInterface $saleableItem
* @param PriceInterface $price
* @param RendererPool $rendererPool
* @param ConfigurableOptionsProviderInterface $configurableOptionsProvider
* @param array $data
* @param LowestPriceOptionsProviderInterface|null $lowestPriceOptionsProvider
* @param SalableResolverInterface|null $salableResolver
* @param MinimalPriceCalculatorInterface|null $minimalPriceCalculator
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function __construct(
Context $context,
SaleableInterface $saleableItem,
PriceInterface $price,
RendererPool $rendererPool,
ConfigurableOptionsProviderInterface $configurableOptionsProvider,
array $data = [],
LowestPriceOptionsProviderInterface $lowestPriceOptionsProvider = null,
SalableResolverInterface $salableResolver = null,
MinimalPriceCalculatorInterface $minimalPriceCalculator = null
) {
parent::__construct($context, $saleableItem, $price, $rendererPool, $data);
$this->salableResolver = $salableResolver ?: ObjectManager::getInstance()->get(SalableResolverInterface::class);
$this->minimalPriceCalculator = $minimalPriceCalculator
?: ObjectManager::getInstance()->get(MinimalPriceCalculatorInterface::class);
$this->lowestPriceOptionsProvider = $lowestPriceOptionsProvider ?:
ObjectManager::getInstance()->get(LowestPriceOptionsProviderInterface::class);
}
/**
* @return string
*/
protected function _toHtml()
{
$result = parent::_toHtml();
//Renders MSRP in case it is enabled
if ($this->isMsrpPriceApplicable()) {
/** @var BasePriceBox $msrpBlock */
$msrpBlock = $this->rendererPool->createPriceRender(
MsrpPrice::PRICE_CODE,
$this->getSaleableItem(),
[
'real_price_html' => $result,
'zone' => $this->getZone(),
]
);
$result = $msrpBlock->toHtml();
}
return $this->wrapResult($result);
}
/**
* Check is MSRP applicable for the current product.
*
* @return bool
*/
protected function isMsrpPriceApplicable()
{
try {
/** @var MsrpPrice $msrpPriceType */
$msrpPriceType = $this->getSaleableItem()->getPriceInfo()->getPrice('msrp_price');
} catch (\InvalidArgumentException $e) {
$this->_logger->critical($e);
return false;
}
$product = $this->getSaleableItem();
return $msrpPriceType->canApplyMsrp($product) && $msrpPriceType->isMinimalPriceLessMsrp($product);
}
/**
* Wrap with standard required container
*
* @param string $html
* @return string
*/
protected function wrapResult($html)
{
return '<div class="price-box ' . $this->getData('css_classes') . '" ' .
'data-role="priceBox" ' .
'data-product-id="' . $this->getSaleableItem()->getId() . '" ' .
'data-price-box="product-id-' . $this->getSaleableItem()->getId() . '"' .
'>' . $html . '</div>';
}
/**
* Render minimal amount
*
* @return string
*/
public function renderAmountMinimal()
{
$id = $this->getPriceId() ? $this->getPriceId() : 'product-minimal-price-' . $this->getSaleableItem()->getId();
$amount = $this->minimalPriceCalculator->getAmount($this->getSaleableItem());
if ($amount === null) {
return '';
}
return $this->renderAmount(
$amount,
[
'display_label' => __('As low as'),
'price_id' => $id,
'include_container' => false,
'skip_adjustments' => true
]
);
}
/**
* Define if the special price should be shown
*
* @return bool
*/
public function hasSpecialPrice()
{
$product = $this->getSaleableItem();
foreach ($this->lowestPriceOptionsProvider->getProducts($product) as $subProduct) {
$regularPrice = $subProduct->getPriceInfo()->getPrice(RegularPrice::PRICE_CODE)->getValue();
$finalPrice = $subProduct->getPriceInfo()->getPrice(FinalPrice::PRICE_CODE)->getValue();
if ($finalPrice < $regularPrice) {
return true;
}
}
return false;
}
/**
* Define if the minimal price should be shown
*
* @return bool
*/
public function showMinimalPrice()
{
$minTierPrice = $this->minimalPriceCalculator->getValue($this->getSaleableItem());
/** @var Price\FinalPrice $finalPrice */
$finalPrice = $this->getPriceType(Price\FinalPrice::PRICE_CODE);
$finalPriceValue = $finalPrice->getAmount()->getValue();
return $this->getDisplayMinimalPrice()
&& $minTierPrice !== null
&& $minTierPrice < $finalPriceValue;
}
/**
* Get Key for caching block content
*
* @return string
*/
public function getCacheKey()
{
return parent::getCacheKey() . ($this->getData('list_category_page') ? '-list-category-page': '');
}
/**
* {@inheritdoc}
*
* @return array
*/
public function getCacheKeyInfo()
{
$cacheKeys = parent::getCacheKeyInfo();
$cacheKeys['display_minimal_price'] = $this->getDisplayMinimalPrice();
$cacheKeys['is_product_list'] = $this->isProductList();
return $cacheKeys;
}
/**
* Get flag that price rendering should be done for the list of products
* By default (if flag is not set) is false
*
* @return bool
*/
public function isProductList()
{
$isProductList = $this->getData('is_product_list');
return $isProductList === true;
}
}
In this file we change in _toHtml() function whose return blank HTML content if product is not salable.
Create file catalog_product_prices.xml in app/code/Vendor/Module/view/base/layout folder with the following code.
<?xml version="1.0"?>
<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd">
<referenceBlock name="render.product.prices">
<arguments>
<argument name="configurable" xsi:type="array">
<item name="prices" xsi:type="array">
<item name="final_price" xsi:type="array">
<item name="render_class" xsi:type="string">Vendor\Module\Pricing\Render\FinalPriceBox</item>
<item name="render_template" xsi:type="string">Magento_ConfigurableProduct::product/price/final_price.phtml</item>
</item>
</item>
</argument>
</arguments>
</referenceBlock>
</layout>
In this file we change render_class for call our custom class.
Now execute the below command and go to product detail page or product listing page:
php bin/magento c:c
For anyone trying to do the same on Magento 2.4.2 commenting out the line below worked for us.
$this->baseSelectProcessor->process($select);
On file /vendor/magento/module-configurable-product/Model/ResourceModel/Product/Indexer/Price/Configurable.php
private function fillTemporaryOptionsTable(string $temporaryOptionsTableName, array $dimensions, array $entityIds)
{
$metadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
$linkField = $metadata->getLinkField();
$select = $this->getConnection()->select()->from(
['i' => $this->getMainTable($dimensions)],
[]
)->join(
['l' => $this->getTable('catalog_product_super_link')],
'l.product_id = i.entity_id',
[]
)->join(
['le' => $this->getTable('catalog_product_entity')],
'le.' . $linkField . ' = l.parent_id',
[]
);
//$this->baseSelectProcessor->process($select);
$select->columns(
[
'le.entity_id',
'customer_group_id',
'website_id',
'MIN(final_price)',
'MAX(final_price)',
'MIN(tier_price)',
]
)->group(
['le.entity_id', 'customer_group_id', 'website_id']
);
if ($entityIds !== null) {
$select->where('le.entity_id IN (?)', $entityIds, \Zend_Db::INT_TYPE);
}
$this->tableMaintainer->insertFromSelect($select, $temporaryOptionsTableName, []);
}
My idea is get simple with lowest price from configurable, then render price of this simple.
di.xml:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Catalog\Block\Product\ListProduct">
<plugin name="PhungSpse_ConfigurableProduct_Plugin_ListProduct" type="PhungSpse\ConfigurableProduct\Plugin\Block\Product\ListProduct"/>
</type>
</config>
php:
<?php
namespace PhungSpse\ConfigurableProduct\Plugin\Block\Product;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Model\Product;
use Magento\ConfigurableProduct\Pricing\Price\LowestPriceOptionsProviderInterface;
use Magento\Catalog\Block\Product\ListProduct as Subject;
class ListProduct
{
/** @var LowestPriceOptionsProviderInterface */
protected $lowestPriceOptionsProvider;
/**
* ConfigurablePrice constructor.
* @param LowestPriceOptionsProviderInterface $lowestPriceOptionsProvider
*/
public function __construct(
LowestPriceOptionsProviderInterface $lowestPriceOptionsProvider
){
$this->lowestPriceOptionsProvider = $lowestPriceOptionsProvider;
}
/**
* @param Subject $subject
* @param Product $product
* @return array
*/
public function beforeGetProductPrice(Subject $subject, Product $product)
{
$product = $this->getChildProductLowestPrice($product);
return [$product];
}
/**
* @param ProductInterface $parentProduct
* @return ProductInterface
*/
protected function getChildProductLowestPrice($parentProduct)
{
if ($parentProduct->getTypeId() != 'simple') {
$products = $this->lowestPriceOptionsProvider->getProducts($parentProduct);
if (!empty($products)) {
return reset($products);
}
}
return $parentProduct;
}
}
you can try this my simple extension