magento/magento2

CE 2.1.7 Attributes - Display Out of Stock Products

stroblee opened this issue ยท 52 comments

Preconditions

  1. Magento CE 2.1.7
  2. Apache 2.4.27
  3. PHP 7..0.22
  4. Varnish 4.1.8

Steps to reproduce

  1. Install Magento 2.1.7 CE Sample Data
  2. Admin > Stores > Configuration > Catalog > Inventory > Stock Options > Display Out of Stock Products = Yes.
  3. CLI php bin/magento indexer:reindex
  4. Admin > Products > Catalog > Breathe-Easy Tank-XS-Puple, Breathe-Easy Tank-XS-White, Breathe-Easy Tank-XS-Orange. Set either Quantity = 0 or Stock Status = Out of Stock.
  5. CLI php bin/magento cache:clean

Expected result

  1. Frontend product - Breathe-Easy Tank. The swatch attribute Size XS' should display with class disabled (red strike through) for all Simple products part of Configurable product.

Actual result

  1. Frontend product - Breathe-Easy Tank. The swatch attribute Size XS not displayed at all.

Other Information

  1. If one Simple product from Configurable options, i.e. Breathe-Easy Tank-XS-Puple, Breathe-Easy Tank-XS-White, Breathe-Easy Tank-XS-Orange has either Quantity => 1 or Stock Status = In Stock then the remaining out of stock products will display the swatch attribute 'Size XS' with class disabled (red strike through) for all Simple products part of Configurable product.

The issue seems to be in a way how Magento retrieves products for showing as a configurable options.

In the Magento\ConfigurableProduct\Block\Product\View\Type\Configurable:: getAllowProducts method it checks for the getSkipSaleableCheck flag which is false and only set to true during admin checkout.

Since the flag is false, it goes down to the Magento\ConfigurableProduct\Model\Product\Type\Configurable::getSalableUsedProducts where stock status is always validated.

In other words, "Display Out of Stock Products" configuration does not make any difference in building configurable product options.

Any update on this? Major issue for us.

I'm working on it #SQUASHTOBERFEST

Is there any updates on if this has been fixed at all on Magento 2.1.9?

@callumstar no, it's not fixed in 2.2.1 either.

Was assigned to Development in Progress. Then was unassigned. Fun stuff.

I have stumbled across this issue. It seems to be caused by this class: \Magento\ConfigurableProduct\Plugin\Model\ResourceModel\Attribute\InStockOptionSelectBuilder.

This plugin ignores the stock-config check completely. I have overridden this plugin to look like this:
`
public function __construct(Status $stockStatusResource, StockConfigurationInterface $stockConfiguration)
{
$this->stockStatusResource = $stockStatusResource;
$this->stockConfiguration = $stockConfiguration;
}

/**
 * Add stock status filter to select.
 *
 * @param OptionSelectBuilderInterface $subject
 * @param Select $select
 * @return Select
 *
 * @SuppressWarnings(PHPMD.UnusedFormalParameter)
 */
public function afterGetSelect(OptionSelectBuilderInterface $subject, Select $select)
{
    if (!$this->stockConfiguration->isShowOutOfStock()) {
        $select->joinInner(
            ['stock' => $this->stockStatusResource->getMainTable()],
            'stock.product_id = entity.entity_id',
            []
        )->where(
            'stock.stock_status = ?',
            \Magento\CatalogInventory\Model\Stock\Status::STATUS_IN_STOCK
        );
    }

    return $select;
}

}
`

I am not using the swatches, so it may cause that to break. But with using the selectors, it works correctly.

Hi @wejobes,
I tried your solution with swatches and it's working well but the attribute can be selected and is not disabled or something.

@VincentMarmiesse check if you have back orders enabled or not. The inventory tracking will play a role in this when the isSaleable method is called.

@wejobes Back orders are not enabled.

seyuf commented

Here's my solution if someone is interested (for 2.1.9) :

    public function aroundGetSalableUsedProducts(\Magento\ConfigurableProduct\Model\Product\Type\Configurable $subject, \Closure $proceed, Product $product, $requiredAttributeIds = null)
    {


        $isShowOutOfStock = $this->_scopeConfig->isSetFlag(
            'cataloginventory/options/show_out_of_stock',
            \Magento\Store\Model\ScopeInterface::SCOPE_STORE
        );
        $usedProducts = $subject->getUsedProducts($product, $requiredAttributeIds);
        $usedSalableProducts = array_filter($usedProducts, function (Product $product) use ($isShowOutOfStock){
            $stockStatus = $this->stockRegistry->getStockStatus(
                $product->getId(),
                $product->getStore()->getWebsiteId()
            );

            if ($isShowOutOfStock === false) {
                return (int)$stockStatus->getStockStatus() === Status::STATUS_IN_STOCK && $product->isSalable();
            }
            else{
                return $product->isSalable();
            }
        });

        return $usedSalableProducts;
    }

In the contructor:

 /**
     * Configurable constructor.
     * @param ScopeConfigInterface $scopeConfig
     * @param StockRegistryInterface|null $stockRegistry
     */
    public function __construct(
        ScopeConfigInterface $scopeConfig,
        StockRegistryInterface $stockRegistry = null
    ) {

        $this->_scopeConfig = $scopeConfig;
        $this->stockRegistry = $stockRegistry ?: ObjectManager::getInstance()
            ->get(StockRegistryInterface::class);
    }

Any update on this?

We currently have multiple clients who would love this feature, I hope it will be included/fixed soon...

Seems a similar issue : #15047

Still problem in 2.2.4, is anyone working on this?

Bump

Bump

someone find the issue?

@jeffb1a5 @maierb @JoshuaSpeechleyRS @joost-heijden @Rann1 @emreozandac Give the comments on this PR a read #12936

I've been discussing with the author of the PR the problem which relates to this issue.

I started writing a small extension yesterday which will render all out of stock swatches/options/children - putting the finishing touches on it today, i'll throw it up on Github if you guys will find it useful.

@josh-carter Would love to see the extension if you have a chance to upload it. It would relieve so much headache with this small but significant issue.

@josh-carter, yea looking forward to use your extension.

@joost-heijden @skilar Apologies for the delay - the extension is at https://github.com/interjar/configurable-child-visibility

Now, its not going to do absolutely everything you might need but it will do the majority of things. Also, it does rely on the Display out of Stock Products to be set to Yes.

If you want to show stuff like strikethroughs through swatches etc. You'll need to (or get you're developer to) sort that. If you want to show all swatches even when every child is out of stock on the product view page, theres a slight template amend needed in the product view form template of your theme. But its very minor.

Let me know if you need anything else. I'm going to try and get these issues sorted in the core either way of the next few weeks. I'll try and keep you guys posted.

This is just what I need. How would you add strikethroughs through swatches?

@jeffb1a5 I've actually done this in a separate extension myself. You'd have to create one and add some sort of identifier to the JSON thats rendered JsonSwatchConfig i think it'd be. You'd then look at setting a flag maybe to identify the out of stock selections, then based off that in the JS add a class onto the swatch elements and handle the strikethrough with CSS.

Thats one way to go for it. I could maybe package this up and release it on GitHub too, but ultimately i hope to get fixes in the core for the configurable child products visibility. So that extension i've put up would then become redundant.

If i get some time over the next couple of days i'll get some sort of extension for striking through the swatches.

any progress on the strike throughs (hopefully!)

piwni commented

Hey guys, I've made a fork of Josh's solution and extended it to show the sold out variants as disabled: take a look at this pull request: interjar/configurable-child-visibility#4 . Works like a charm on 2.2.5.

@jeffb1a5 @maierb @JoshuaSpeechleyRS @joost-heijden @Rann1 @emreozandac @skilar

how is this on 2.2.6?

@piwni I tried your fork on 2.2.6. but it goes back to hiding the option alltogether. What would be the correct way to implement it?

@josh-carter i've use your extension, but it doesn't work on magento 2.2.5
I've try @piwni commit as well

piwni commented

It seems to me that there may be more things that need to be taken into consideration. I'll take a look at it in a free while.

@piwni thanks you, i've searching for this issue all day long and find a lot of code and suggestion, but none of them work so far.

I'm also having this problem in magento2.3

// I have found that the configurable $options data is initially set here
// I have made it so that this returns the entire array of attribute codes and product numbers -
//  - for the configurable regardless of stock status 
/* @var \Magento\ConfigurableProduct\Helper\Data $this->$helper */
$options = $this->helper->getOptions($currentProduct, $this->getAllowProducts());

Note that the $options[array] correctly contains all out of stock attribute ids => product ids

$options array

This $options array is passed to a function that attempts to serialize/add extra data that is required buy the js renderer such as labels.

$attributesData = $this->configurableAttributeData->getAttributesData($currentProduct, $options);

/**
     * Get product attributes
     *
     * @param Product $product
     * @param array $options
     * @return array
     */
    public function getAttributesData(Product $product, array $options = [])
    {
        $defaultValues = [];
        $attributes = [];
        $productInstance = $product->getTypeInstance();
        $productEnabledAttributes = $productInstance ->getConfigurableAttributes($product);
        foreach ($productEnabledAttributes as $attribute) {
            $attributeOptionsData = $this->getAttributeOptionsData($attribute, $options);
            if ($attributeOptionsData) {
                $productAttribute = $attribute->getProductAttribute();
                $attributeId = $productAttribute->getId();
                $attributes[$attributeId] = [
                    'id' => $attributeId,
                    'code' => $productAttribute->getAttributeCode(),
                    'label' => $productAttribute->getStoreLabel($product->getStoreId()),
                    'options' => $attributeOptionsData,
                    'position' => $attribute->getPosition(),
                ];
                $defaultValues[$attributeId] = $this->getAttributeConfigValue($attributeId, $product);
            }
        }
        return [
            'attributes' => $attributes,
            'defaultValues' => $defaultValues,
        ];
    }

However getAttributesData does not obey the $options param. A sub call function must unwittingly run a filter operation. Observe below how the return $attributesData has filtered attribute "138"

data filtered

From the getAttributesData() function, I'm suspecting getConfigurableAttributes() to be the cause of out of stock options not displaying in magento 2.3, this function retrieves data from a cache that was generated a few clock cycles ago.

/**
     * Retrieve configurable attributes data
     *
     * @param  \Magento\Catalog\Model\Product $product
     * @return \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute[]
     */
    public function getConfigurableAttributes($product)
    {
        \Magento\Framework\Profiler::start(
            'CONFIGURABLE:' . __METHOD__,
            ['group' => 'CONFIGURABLE', 'method' => __METHOD__]
        );
        if (!$product->hasData($this->_configurableAttributes)) {
            $configurableAttributes = $this->getConfigurableAttributeCollection($product);
            $this->extensionAttributesJoinProcessor->process($configurableAttributes);
            $configurableAttributes->orderByPosition()->load();
            $product->setData($this->_configurableAttributes, $configurableAttributes);
        }
        \Magento\Framework\Profiler::stop('CONFIGURABLE:' . __METHOD__);
        return $product->getData($this->_configurableAttributes);
    }

Really confused atm. That stack trace is insane.

image

There's a configuration available in admin that says "Show out of stock product"

I'm under the impression that this should also apply to configurable products, however the js render code doesn't seem to disable options that are out of stock in selection. This is perhaps why there's two plugins dedicated to making sure out of stock options don't show.

Culprit plugin locations:

  • MODULE Magento_ConfigurableProduct -> AttributeValidation.php
  • MODULE Magento_InventoryConfigurableProduct -> IsSalableOptionSelectBuilder.php

Since those plugins weren't following the "Show out of stock product" configuration, I added an afterGetSelect plugin checks for the "Show out of stock product" config.

I changed the Json builder to send preorder data and in stock information to my override of the configurable product render code.

Result: This is what I was trying to do: https://i.imgur.com/mk1lNve.png

this explains what the culprit plugins do:

https://i.imgur.com/J7vsY2G.png

'stock check added here' https://i.imgur.com/vnJ4oqA.png

'aaaannnndddd here again for good measure'https://i.imgur.com/Jr6TSBb.png

TLDR: Some one at magento doubly doesn't want people getting out of stock options. Configurable SQL table layout is really complex.

seyuf commented

Hi @PathToLife,
you're saying that this issue still exist in 2.3 despite MSI? (I was under the impression that this was fixed there). Or are you not using MSI?

Yep MSI is enabled, but we're not using it.

Regardless the culprit plugins should have a check for the "Show out of stock product" (Store ID basis) configuration anyhow.

And the configurable js code might need updating so dropdown options are greyed out / show out of stock message

seyuf commented

Ha alright, thanks for the info.
I will check with MSI to see if these issues still exists as MSI is the recommended option on m 2.3.

Otherwise i m not sure, but i believe some of your work was already done in the plugin mentioned above(https://github.com/interjar/configurable-child-visibility).
As for the customization of the out stock options display, im not sure that is necessarily a core functionality/feature, for in any case if the user try to order and out of stock option he will be prompted with the according error msg.

Anyway good job ๐Ÿ‘

I'm also having this problem in magento2.3

// I have found that the configurable $options data is initially set here
// I have made it so that this returns the entire array of attribute codes and product numbers -
//  - for the configurable regardless of stock status 
/* @var \Magento\ConfigurableProduct\Helper\Data $this->$helper */
$options = $this->helper->getOptions($currentProduct, $this->getAllowProducts());

Note that the $options[array] correctly contains all out of stock attribute ids => product ids

$options array

This $options array is passed to a function that attempts to serialize/add extra data that is required buy the js renderer such as labels.

$attributesData = $this->configurableAttributeData->getAttributesData($currentProduct, $options);

/**
     * Get product attributes
     *
     * @param Product $product
     * @param array $options
     * @return array
     */
    public function getAttributesData(Product $product, array $options = [])
    {
        $defaultValues = [];
        $attributes = [];
        $productInstance = $product->getTypeInstance();
        $productEnabledAttributes = $productInstance ->getConfigurableAttributes($product);
        foreach ($productEnabledAttributes as $attribute) {
            $attributeOptionsData = $this->getAttributeOptionsData($attribute, $options);
            if ($attributeOptionsData) {
                $productAttribute = $attribute->getProductAttribute();
                $attributeId = $productAttribute->getId();
                $attributes[$attributeId] = [
                    'id' => $attributeId,
                    'code' => $productAttribute->getAttributeCode(),
                    'label' => $productAttribute->getStoreLabel($product->getStoreId()),
                    'options' => $attributeOptionsData,
                    'position' => $attribute->getPosition(),
                ];
                $defaultValues[$attributeId] = $this->getAttributeConfigValue($attributeId, $product);
            }
        }
        return [
            'attributes' => $attributes,
            'defaultValues' => $defaultValues,
        ];
    }

However getAttributesData does not obey the $options param. A sub call function must unwittingly run a filter operation. Observe below how the return $attributesData has filtered attribute "138"

data filtered

From the getAttributesData() function, I'm suspecting getConfigurableAttributes() to be the cause of out of stock options not displaying in magento 2.3, this function retrieves data from a cache that was generated a few clock cycles ago.

/**
     * Retrieve configurable attributes data
     *
     * @param  \Magento\Catalog\Model\Product $product
     * @return \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute[]
     */
    public function getConfigurableAttributes($product)
    {
        \Magento\Framework\Profiler::start(
            'CONFIGURABLE:' . __METHOD__,
            ['group' => 'CONFIGURABLE', 'method' => __METHOD__]
        );
        if (!$product->hasData($this->_configurableAttributes)) {
            $configurableAttributes = $this->getConfigurableAttributeCollection($product);
            $this->extensionAttributesJoinProcessor->process($configurableAttributes);
            $configurableAttributes->orderByPosition()->load();
            $product->setData($this->_configurableAttributes, $configurableAttributes);
        }
        \Magento\Framework\Profiler::stop('CONFIGURABLE:' . __METHOD__);
        return $product->getData($this->_configurableAttributes);
    }

Really confused atm. That stack trace is insane.

image

I am also facing this problem here, Did you get any solution ?

Still occurs in Magento 2.3

The above module fix does not work for 2.3

Any movement on this or suggestions?

Still exist in Magneto 2.3, I have tested in 2.3-develop branch. Is there any fix for this issue?

Have there been any updates on this bug?

@josh-carter :

What about you? Your repository was installed more than 6000 times. Are you planning an update for https://github.com/interjar/configurable-child-visibility to be compatible with magento 2.3?

@magento:
This should be fixed. It's important to show the users which products are generally available even if they are out of stock for the moment.

@magento This is an extremely basic requirement for any e-commerce platform. How has this bug been allowed to remain unresolved for over two years? This is seriously worrying; if something this fundamental isn't fixed, what other problems might be lurking within the platform?

Hello everyone!
We are not able to reproduce this issue on the latest 2.3-develop(with Varnish 4) branch by provided steps.

Manual testing scenario:

  1. Install Magento Sample Data
  2. Admin > Stores > Configuration > Catalog > Inventory > Stock Options > Display Out of Stock Products = Yes.
  3. CLI php bin/magento indexer:reindex
  4. Admin > Products > Catalog > Breathe-Easy Tank-XS-Puple, Breathe-Easy Tank-XS-White, Breathe-Easy Tank-XS-Orange. Set either Quantity = 0 or Stock Status = Out of Stock.
  5. CLI php bin/magento cache:clean

Result:
Frontend product - Breathe-Easy Tank. The swatch attribute Size XS' should display with class disabled (red strike through) for all Simple products part of Configurable product.
image

So i have to close this issue.

Have you tested this with configurable products only having one attribute varient?
For example, a product with a variant of size

Related
#15047

Is there a solution for 2.3?

It seems like the confusion stems from if there's one colour or multiple colour choices. If there is only one colour choice available, the sold out size will be hidden. Whereas if there are multiple colour choices available, the sold out size will be striked when the sold out colour is selected.

Personally, I would like the option to always show the sold out size, regardless of the number of colours available.

@engcom-Charlie Why close this issue instead of seeking clarification? This is still a problem in 2.4! This is the kind of thing making me really frustrated with Magento.

@diamondavocado @palomamtnez Did you find a solution for this?

This has been closed incorrectly. See @toby-pondr's comment. This is only an issue if the product only has one color.

@stroblee looks like only the original author can re-open this. Mind re-opening?

@engcom-Charlie @magento-engcom-team this closure is based on a misunderstanding of the issue at hand here, see response from @MichaelThessel and the recently opened issue by them also #30046.

The intention is clearly to show out stock swatch options, the screenshot demonstrates this:

But this doesn't happen when there is only one attribute, so it is only partially working.

Helloooooo,
2.4.3 and still wait for this. Why is it closed ?