addtocart.phtml
<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ // @codingStandardsIgnoreFile /** @var $block \Magento\Catalog\Block\Product\View */ ?> <?php $_product = $block->getProduct(); ?> <?php $buttonTitle = __('Add to Cart'); ?> <?php if ($_product->isSaleable()): ?> <div class="box-tocart"> <div class="fieldset"> <?php if ($block->shouldRenderQuantity()): $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $StockState = $objectManager->get('\Magento\CatalogInventory\Api\StockStateInterface');?> <div class="field qty"> <label class="label" for="qty"><span><?= /* @escapeNotVerified */ __('Qty') ?></span></label> <div class="control"> <?php if($_product->getTypeId()=="simple") { ?> <select class="input-text qty" name="qty" id="qty" data-validate="<?= $block->escapeHtml(json_encode($block->getQuantityValidators())) ?>"> <?php $availableQty= $StockState->getStockQty($_product->getId(), $_product->getStore()->getWebsiteId()); for($repli=1;$repli<=$availableQty;$repli++) echo "<option value='$repli'>$repli</option>"; ?> </select> <?php } else { $usedProducts = $_product->getTypeInstance()->getUsedProducts($_product); $productStockd=$objectManager->get('Magento\CatalogInventory\Api\StockRegistryInterface'); $finalconfigqty=[]; foreach ($usedProducts as $child) {$childid=$child->getId(); $productStockObj = $productStockd->getStockItem($childid); $availableQty= round($productStockObj->getData('qty')); $finalconfigqty['c'.$childid]=$availableQty; } echo '<script> var fichoo='. json_encode($finalconfigqty). '</script>'; echo '<select class="input-text qty fichoo" name="qty" id="qty"><option value=1>1</option></select>'; ?> <!--input type="number" name="qty" id="qty" value="<?= $block->getProductDefaultQty() * 1 ?>" title="<?= __('Qty') ?>" class="input-text qty" data-validate="<?= $block->escapeHtml(json_encode($block->getQuantityValidators())) ?>" /--> <?php } ?> </div> </div> <?php endif; ?> <div class="actions"> <button type="submit" title="<?= /* @escapeNotVerified */ $buttonTitle ?>" class="action primary tocart" id="product-addtocart-button"> <span><?= /* @escapeNotVerified */ $buttonTitle ?></span> </button> <?= $block->getChildHtml('', true) ?> </div> </div> </div> <?php endif; ?> <?php if ($block->isRedirectToCartEnabled()) : ?> <script type="text/x-magento-init"> { "#product_addtocart_form": { "Magento_Catalog/product/view/validation": { "radioCheckboxClosest": ".nested" } } } </script> <?php else : ?> <script type="text/x-magento-init"> { "#product_addtocart_form": { "Magento_Catalog/js/validate-product": {} } } </script> <?php endif; ?>
Another important file is Magento_ConfigurableProduct/web/js/configurable.js
modified function _getSimpleProductId and added function _dofichoo
/** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ /** * @api */ define([ 'jquery', 'underscore', 'mage/template', 'mage/translate', 'priceUtils', 'priceBox', 'jquery/ui', 'jquery/jquery.parsequery' ], function ($, _, mageTemplate, $t, priceUtils) { 'use strict'; $.widget('mage.configurable', { options: { superSelector: '.super-attribute-select', selectSimpleProduct: '[name="selected_configurable_option"]', priceHolderSelector: '.price-box', spConfig: {}, state: {}, priceFormat: {}, optionTemplate: '<%- data.label %>' + '<% if (typeof data.finalPrice.value !== "undefined") { %>' + ' <%- data.finalPrice.formatted %>' + '<% } %>', mediaGallerySelector: '[data-gallery-role=gallery-placeholder]', mediaGalleryInitial: null, slyOldPriceSelector: '.sly-old-price', /** * Defines the mechanism of how images of a gallery should be * updated when user switches between configurations of a product. * * As for now value of this option can be either 'replace' or 'prepend'. * * @type {String} */ gallerySwitchStrategy: 'replace', tierPriceTemplateSelector: '#tier-prices-template', tierPriceBlockSelector: '[data-role="tier-price-block"]', tierPriceTemplate: '' }, /** * Creates widget * @private */ _create: function () { // Initial setting of various option values this._initializeOptions(); // Override defaults with URL query parameters and/or inputs values this._overrideDefaults(); // Change events to check select reloads this._setupChangeEvents(); // Fill state this._fillState(); // Setup child and prev/next settings this._setChildSettings(); // Setup/configure values to inputs this._configureForValues(); $(this.element).trigger('configurable.initialized'); }, /** * Initialize tax configuration, initial settings, and options values. * @private */ _initializeOptions: function () { var options = this.options, gallery = $(options.mediaGallerySelector), priceBoxOptions = $(this.options.priceHolderSelector).priceBox('option').priceConfig || null; if (priceBoxOptions && priceBoxOptions.optionTemplate) { options.optionTemplate = priceBoxOptions.optionTemplate; } if (priceBoxOptions && priceBoxOptions.priceFormat) { options.priceFormat = priceBoxOptions.priceFormat; } options.optionTemplate = mageTemplate(options.optionTemplate); options.tierPriceTemplate = $(this.options.tierPriceTemplateSelector).html(); options.settings = options.spConfig.containerId ? $(options.spConfig.containerId).find(options.superSelector) : $(options.superSelector); options.values = options.spConfig.defaultValues || {}; options.parentImage = $('[data-role=base-image-container] img').attr('src'); this.inputSimpleProduct = this.element.find(options.selectSimpleProduct); gallery.data('gallery') ? this._onGalleryLoaded(gallery) : gallery.on('gallery:loaded', this._onGalleryLoaded.bind(this, gallery)); }, /** * Override default options values settings with either URL query parameters or * initialized inputs values. * @private */ _overrideDefaults: function () { var hashIndex = window.location.href.indexOf('#'); if (hashIndex !== -1) { this._parseQueryParams(window.location.href.substr(hashIndex + 1)); } if (this.options.spConfig.inputsInitialized) { this._setValuesByAttribute(); } }, /** * Parse query parameters from a query string and set options values based on the * key value pairs of the parameters. * @param {*} queryString - URL query string containing query parameters. * @private */ _parseQueryParams: function (queryString) { var queryParams = $.parseQuery({ query: queryString }); $.each(queryParams, $.proxy(function (key, value) { this.options.values[key] = value; }, this)); }, /** * Override default options values with values based on each element's attribute * identifier. * @private */ _setValuesByAttribute: function () { this.options.values = {}; $.each(this.options.settings, $.proxy(function (index, element) { var attributeId; if (element.value) { attributeId = element.id.replace(/[a-z]*/, ''); this.options.values[attributeId] = element.value; } }, this)); }, /** * Set up .on('change') events for each option element to configure the option. * @private */ _setupChangeEvents: function () { $.each(this.options.settings, $.proxy(function (index, element) { $(element).on('change', this, this._configure); }, this)); }, _dofichoo: function(thisstock){var lsstr=''; for(var ah=1;ah<=window.fichoo['c'+thisstock];ah++) lsstr+='<option value='+ah+ ' >'+ ah + '</option>'; $('select.fichoo').html(lsstr); }, /** * Iterate through the option settings and set each option's element configuration, * attribute identifier. Set the state based on the attribute identifier. * @private */ _fillState: function () { $.each(this.options.settings, $.proxy(function (index, element) { var attributeId = element.id.replace(/[a-z]*/, ''); if (attributeId && this.options.spConfig.attributes[attributeId]) { element.config = this.options.spConfig.attributes[attributeId]; element.attributeId = attributeId; this.options.state[attributeId] = false; } }, this)); }, /** * Set each option's child settings, and next/prev option setting. Fill (initialize) * an option's list of selections as needed or disable an option's setting. * @private */ _setChildSettings: function () { var childSettings = [], settings = this.options.settings, index = settings.length, option; while (index--) { option = settings[index]; if (index) { option.disabled = true; } else { this._fillSelect(option); } _.extend(option, { childSettings: childSettings.slice(), prevSetting: settings[index - 1], nextSetting: settings[index + 1] }); childSettings.push(option); } }, /** * Setup for all configurable option settings. Set the value of the option and configure * the option, which sets its state, and initializes the option's choices, etc. * @private */ _configureForValues: function () { if (this.options.values) { this.options.settings.each($.proxy(function (index, element) { var attributeId = element.attributeId; element.value = this.options.values[attributeId] || ''; this._configureElement(element); }, this)); } }, /** * Event handler for configuring an option. * @private * @param {Object} event - Event triggered to configure an option. */ _configure: function (event) { event.data._configureElement(this); }, /** * Configure an option, initializing it's state and enabling related options, which * populates the related option's selection and resets child option selections. * @private * @param {*} element - The element associated with a configurable option. */ _configureElement: function (element) { this.simpleProduct = this._getSimpleProductId(element); if (element.value) { this.options.state[element.config.id] = element.value; if (element.nextSetting) { element.nextSetting.disabled = false; this._fillSelect(element.nextSetting); this._resetChildren(element.nextSetting); } else { if (!!document.documentMode) { //eslint-disable-line this.inputSimpleProduct.val(element.options[element.selectedIndex].config.allowedProducts[0]); } else { this.inputSimpleProduct.val(element.selectedOptions[0].config.allowedProducts[0]); } } } else { this._resetChildren(element); } this._reloadPrice(); this._displayRegularPriceBlock(this.simpleProduct); this._displayTierPriceBlock(this.simpleProduct); this._changeProductImage(); }, /** * Change displayed product image according to chosen options of configurable product * * @private */ _changeProductImage: function () { var images, initialImages = this.options.mediaGalleryInitial, galleryObject = $(this.options.mediaGallerySelector).data('gallery'); if (!galleryObject) { return; } images = this.options.spConfig.images[this.simpleProduct]; if (images) { if (this.options.gallerySwitchStrategy === 'prepend') { images = images.concat(initialImages); } images = $.extend(true, [], images); images = this._setImageIndex(images); galleryObject.updateData(images); $(this.options.mediaGallerySelector).AddFotoramaVideoEvents({ selectedOption: this.simpleProduct, dataMergeStrategy: this.options.gallerySwitchStrategy }); } else { galleryObject.updateData(initialImages); $(this.options.mediaGallerySelector).AddFotoramaVideoEvents(); } galleryObject.first(); }, /** * Set correct indexes for image set. * * @param {Array} images * @private */ _setImageIndex: function (images) { var length = images.length, i; for (i = 0; length > i; i++) { images[i].i = i + 1; } return images; }, /** * For a given option element, reset all of its selectable options. Clear any selected * index, disable the option choice, and reset the option's state if necessary. * @private * @param {*} element - The element associated with a configurable option. */ _resetChildren: function (element) { if (element.childSettings) { _.each(element.childSettings, function (set) { set.selectedIndex = 0; set.disabled = true; }); if (element.config) { this.options.state[element.config.id] = false; } } }, /** * Populates an option's selectable choices. * @private * @param {*} element - Element associated with a configurable option. */ _fillSelect: function (element) { var attributeId = element.id.replace(/[a-z]*/, ''), options = this._getAttributeOptions(attributeId), prevConfig, index = 1, allowedProducts, i, j; this._clearSelect(element); element.options[0] = new Option('', ''); element.options[0].innerHTML = this.options.spConfig.chooseText; prevConfig = false; if (element.prevSetting) { prevConfig = element.prevSetting.options[element.prevSetting.selectedIndex]; } if (options) { for (i = 0; i < options.length; i++) { allowedProducts = []; /* eslint-disable max-depth */ if (prevConfig) { for (j = 0; j < options[i].products.length; j++) { // prevConfig.config can be undefined if (prevConfig.config && prevConfig.config.allowedProducts && prevConfig.config.allowedProducts.indexOf(options[i].products[j]) > -1) { allowedProducts.push(options[i].products[j]); } } } else { allowedProducts = options[i].products.slice(0); } if (allowedProducts.length > 0) { options[i].allowedProducts = allowedProducts; element.options[index] = new Option(this._getOptionLabel(options[i]), options[i].id); if (typeof options[i].price !== 'undefined') { element.options[index].setAttribute('price', options[i].prices); } element.options[index].config = options[i]; index++; } /* eslint-enable max-depth */ } } }, /** * Generate the label associated with a configurable option. This includes the option's * label or value and the option's price. * @private * @param {*} option - A single choice among a group of choices for a configurable option. * @return {String} The option label with option value and price (e.g. Black +1.99) */ _getOptionLabel: function (option) { return option.label; }, /** * Removes an option's selections. * @private * @param {*} element - The element associated with a configurable option. */ _clearSelect: function (element) { var i; for (i = element.options.length - 1; i >= 0; i--) { element.remove(i); } }, /** * Retrieve the attribute options associated with a specific attribute Id. * @private * @param {Number} attributeId - The id of the attribute whose configurable options are sought. * @return {Object} Object containing the attribute options. */ _getAttributeOptions: function (attributeId) { if (this.options.spConfig.attributes[attributeId]) { return this.options.spConfig.attributes[attributeId].options; } }, /** * Reload the price of the configurable product incorporating the prices of all of the * configurable product's option selections. */ _reloadPrice: function () { $(this.options.priceHolderSelector).trigger('updatePrice', this._getPrices()); }, /** * Get product various prices * @returns {{}} * @private */ _getPrices: function () { var prices = {}, elements = _.toArray(this.options.settings), hasProductPrice = false; _.each(elements, function (element) { var selected = element.options[element.selectedIndex], config = selected && selected.config, priceValue = {}; if (config && config.allowedProducts.length === 1 && !hasProductPrice) { priceValue = this._calculatePrice(config); hasProductPrice = true; } prices[element.attributeId] = priceValue; }, this); return prices; }, /** * Returns prices for configured products * * @param {*} config - Products configuration * @returns {*} * @private */ _calculatePrice: function (config) { var displayPrices = $(this.options.priceHolderSelector).priceBox('option').prices, newPrices = this.options.spConfig.optionPrices[_.first(config.allowedProducts)]; _.each(displayPrices, function (price, code) { if (newPrices[code]) { displayPrices[code].amount = newPrices[code].amount - displayPrices[code].amount; } }); return displayPrices; }, /** * Returns Simple product Id * depending on current selected option. * * @private * @param {HTMLElement} element * @returns {String|undefined} */ _getSimpleProductId: function (element) { // TODO: Rewrite algorithm. It should return ID of // simple product based on selected options. var allOptions = element.config.options, value = element.value, config; config = _.filter(allOptions, function (option) { return option.id === value; }); config = _.first(config); if(! _.isEmpty(config)) this._dofichoo(_.first(config.allowedProducts)); return _.isEmpty(config) ? undefined : _.first(config.allowedProducts); }, /** * Show or hide regular price block * * @param {*} optionId * @private */ _displayRegularPriceBlock: function (optionId) { if (typeof optionId != 'undefined' && this.options.spConfig.optionPrices[optionId].oldPrice.amount != //eslint-disable-line eqeqeq this.options.spConfig.optionPrices[optionId].finalPrice.amount ) { $(this.options.slyOldPriceSelector).show(); } else { $(this.options.slyOldPriceSelector).hide(); } }, /** * Callback which fired after gallery gets initialized. * * @param {HTMLElement} element - DOM element associated with gallery. */ _onGalleryLoaded: function (element) { var galleryObject = element.data('gallery'); this.options.mediaGalleryInitial = galleryObject.returnCurrentImages(); }, /** * Show or hide tier price block * * @param {*} optionId * @private */ _displayTierPriceBlock: function (optionId) { var options, tierPriceHtml; if (typeof optionId != 'undefined' && this.options.spConfig.optionPrices[optionId].tierPrices != [] // eslint-disable-line eqeqeq ) { options = this.options.spConfig.optionPrices[optionId]; if (this.options.tierPriceTemplate) { tierPriceHtml = mageTemplate(this.options.tierPriceTemplate, { 'tierPrices': options.tierPrices, '$t': $t, 'currencyFormat': this.options.spConfig.currencyFormat, 'priceUtils': priceUtils }); $(this.options.tierPriceBlockSelector).html(tierPriceHtml).show(); } } else { $(this.options.tierPriceBlockSelector).hide(); } } }); return $.mage.configurable; });
Leave a Reply