6

I'm searching the way to preload quantity of each simple product on configurable product detail page. The reason is I want to dynamically show to client "In Stock", "Out of stock" or "Only 1 left" notification for each size+color config which was selected by client.

P.S.: I used color swatches as dropdown and I saw one hidden field which was dynamically change with selected simple product ID. But then I changed to text swatch+image swatch and this field no more changing.

Can anybody show example how to implement this functionality in Magento 2? Thank you

1 Answer 1

8

The thing is to add additional data with quantity to Configurable Product JSON before creating product detail page. Then override swatch-renderer.js to catch clicks on swatches.

Don't forget to include [Vendor]/[Module]/registration.php and [Vendor]/[Module]/etc/module.xml to your extension

1) [Vendor]/[Module]/Block/ConfigurableProduct/Product/View/Type/Configurable.php

File content:

namespace [Vendor]\[Module]\Block\ConfigurableProduct\Product\View\Type;

use Magento\Framework\Json\EncoderInterface;
use Magento\Framework\Json\DecoderInterface;
use Magento\CatalogInventory\Api\StockRegistryInterface;

class Configurable
{

    protected $jsonEncoder;
    protected $jsonDecoder;
    protected $stockRegistry;

    public function __construct(
        EncoderInterface $jsonEncoder,
        DecoderInterface $jsonDecoder,
        StockRegistryInterface $stockRegistry
    ) {

        $this->jsonDecoder = $jsonDecoder;
        $this->jsonEncoder = $jsonEncoder;
        $this->stockRegistry = $stockRegistry;
    }

    // Adding Quantitites (product=>qty)
    public function aroundGetJsonConfig(
        \Magento\ConfigurableProduct\Block\Product\View\Type\Configurable $subject,
        \Closure $proceed
    )
    {
        $quantities = [];
        $config = $proceed();
        $config = $this->jsonDecoder->decode($config);

        foreach ($subject->getAllowProducts() as $product) {
            $stockitem = $this->stockRegistry->getStockItem(
                $product->getId(),
                $product->getStore()->getWebsiteId()
            );
            $quantities[$product->getId()] = $stockitem->getQty();
        }

        $config['quantities'] = $quantities;

        return $this->jsonEncoder->encode($config);
    }
}

2) [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">
    <type name="Magento\ConfigurableProduct\Block\Product\View\Type\Configurable">
        <plugin name="[vendor]_configurable_product_configurable"
                type="[Vendor]\[Module]\Block\ConfigurableProduct\Product\View\Type\Configurable"
                sortOrder="1"/>
    </type>
</config>

3) [Vendor]/[Module]/view/frontend/requirejs-config.js

var config = {
    map: {
        '*': {
            'Magento_Swatches/js/swatch-renderer':'[Vendor]_[Module]/js/swatch-renderer'
        }
    }
};

4) Copy original file /module-swatches/view/frontend/web/js/swatch-renderer.js to [Vendor]/[Module]/view/frontend/web/js/swatch-renderer.js

In function _OnClick insert before $widget._Rebuild(); code below:

var errorFlag = true;
var selectedSwatch = {};

// Compare objects
function isEquivalent(a, b) {
    // Create arrays of property names
    var aProps = Object.getOwnPropertyNames(a);
    var bProps = Object.getOwnPropertyNames(b);

    // If number of properties is different,
    // objects are not equivalent
    if (aProps.length != bProps.length) {
        return false;
    }

    for (var i = 0; i < aProps.length; i++) {
        var propName = aProps[i];

        // If values of same property are not equal,
        // objects are not equivalent
        if (a[propName] !== b[propName]) {
            return false;
        }
    }

    // If we made it this far, objects
    // are considered equivalent
    return true;
}

// Foreach every attribute and check. If only one undefined (not selected) - nothing won't work
$('.swatch-attribute').each(function () {
    var $element = $(this),
        attrId = $element.attr('attribute-id'),
        optionSelected = $element.attr('option-selected');

    if(typeof optionSelected == "undefined"){
        errorFlag = true;
        return false;
    } else {
        selectedSwatch[attrId] = optionSelected;
        errorFlag = false;
    }
});

// Show stock notification to user
if(errorFlag == false){
    var allTheProducts = $widget.options.jsonConfig.index;
    $.each(allTheProducts, function(index, value) {
        if(isEquivalent(selectedSwatch, value)){
            // Quantity of selected simple product by ID
            var qty = $widget.options.jsonConfig.quantities[index];
            console.log('Simple product ID: '+index+', quantity: '+qty);

            if(qty == 1){
                $('.stock-notification').html("ONLY 1 LEFT IN STOCK");
            } else if(qty > 1){
                $('.stock-notification').html("IN STOCK");
            } else {
                $('.stock-notification').html("OUT OF STOCK");
            }
        }
    });
} else {
    $('.stock-notification').html("");
}
1
  • Use Magento\InventorySalesApi\Api\GetProductSalableQtyInterface for 2.3. $qty = $this->salable->execute($product->getSku(), 1);
    – Dave
    Commented Nov 9, 2020 at 6:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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