7

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?

3
  • Whats the status of product you have set ? is it "out of stock" ? from backend ? Jan 30, 2017 at 4:59
  • yes, when the product is out of stock, product price is not visible in frontend
    – Kevin
    Jan 30, 2017 at 8:52
  • make product status from backend as "in stock" and empty quantity field and then check Jan 30, 2017 at 11:49

7 Answers 7

3

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;
    }
}
5
  • It helped me and Upvoted.
    – Jarnail S
    May 11, 2017 at 5:59
  • is there opposite feature - not to show prices of "out of stock" items ?
    – Gediminas
    Jun 28, 2017 at 7:56
  • The same issue (Website shows products with 0 price and these are the configurable products having all out of stock simple products) happening with me when I upgraded my Magento version from 2.1.0 to 2.1.7 Please someone help Jun 30, 2017 at 10:33
  • 2
    What about configurable product ? It doesn't show any price.
    – ahsan
    Dec 5, 2018 at 22:44
  • Did not work -v mag2.3.5, anyone find a workaround for 2.3.5 ? Jun 3, 2021 at 13:14
7

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;
    }
}
4
  • 1
    Your solution works to show price of configurable product, but it doesn't work for price filters. Filters seem to get their data from 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?
    – Lez
    Nov 23, 2020 at 8:13
  • 1
    Hello @Lez, I'm having the same issue here did you find a fix ? Thanks !
    – magentodev
    Jan 11, 2021 at 9:22
  • @magentodev I tried to solve this by myself, but was not able to track it down completely yet unfortunately.
    – Lez
    Jan 11, 2021 at 10:19
  • Worked for simple products but could not get the 'sub-products out of stock' part working properly, maybe instruction were not clean enough, maybe out of date, used @Mario answer and this worked for me 🥳
    – 00-BBB
    May 20, 2021 at 14:03
5

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.

7
  • Tried several solutions that did not work or only partly worked and finally found this, thank goodness, cheers cheers cheers!!! Nice touch with the store scope swatch option too 😎
    – 00-BBB
    May 20, 2021 at 14:06
  • @Mario I Like this, am having issues with configurable products however, some seem to have difficulties showing all available configurations.
    – harri
    Apr 7 at 12:42
  • hmm...maybe you could open an issue and explain it in more detail? That would make things easier for me to reproduce...
    – Mario
    Apr 8 at 13:04
  • This solution is perfectly working but the price becomes zero. Jul 22 at 7:10
  • @RutvikMonpara hmm - is the price for the configurable product maybe Zero instead of empty?
    – Mario
    Jul 22 at 7:49
2

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

3
  • 1
    Well i want find solution too for this...
    – darkattack
    Oct 4, 2018 at 14:38
  • @Mario's module works, tested on 2.3.1
    – 00-BBB
    May 20, 2021 at 15:12
  • it partially works it does snow price for configurable but all 0.00. any suggestion 2.3.5 ? Jun 3, 2021 at 13:36
1

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

-1

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, []);
}
-1

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

https://github.com/phungspse/configurable-product

1
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From Review Mar 21 at 9:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.