How to migrate Customers from Magento2 to WordPress?

Lets break it into 2 parts:

a. Migrate Customer data
b. Migrate Customer Passwords

1) Migrate Customer Data from Magento2 to WordPress

This is rather easy to be honest, there are several plugins for importing data from CSV and then mapping the CSV fields.

My recommended plugin is Customer/Order/Coupon CSV Import Suite by SkyVerge

It basically does everything from customer email to customer address data. It also provides column mapping and custom attribute import for all users. Also comes with multiple configuration options.

Best Free Alternative is https://wordpress.org/plugins/import-users-from-csv-with-meta
But in this one, you can not import magento2 passwords as of v 1.15.0.1
I will update about the solution very soon. So as of now only paid option is there.

2) Migrate Customer Password from Magento2 to WordPress

This wordpress plugin checks and updates passwords of users migrated from Magento2 to WordPress
Plugin Link: https://github.com/harshvardhanmalpani/migrate-password-from-magento2-to-wordpress

Requirements:

  1. user_pass column will have this kind data from Magento2 dcbdc524f215fd054502dcad5a23a702ec029c02ff8d7051d049f76e29927f8b:C8yVqeuPfkHWvkmipx0iKLPtOUGETpLL:1
  2. usermeta table must have a meta_key “migrated_cs” for this user, meta_value can be anything positive
  3. This plugin file password-migrator.php should be in wp-content/plugins and must be an active plugin

What is does?

For new wordpress based users which dont have anything to do with magento2, it doesnt do anything

For all migrated users (those have the key “migrated_cs”), this plugin checks if input password matches old password from magento2 or not, if matched, it clears the key migrated_cs and updates the password using wordpress’ algorithm ; else results false.

Magento 2 cli commands [continued]

1. Setting GD2 as defualt image adapter from command line 

Magento >= 2.2
php bin/magento config:set dev/image/default_adapter GD2
php bin/magento cache:clean
Magento >= 2.2
 mysql --host=(host) --user=(user) --password=(password) -D (database) -e "\ UPDATE core_config_data SET value = 'GD2' WHERE 'dev/image/default_adapter' = path; \"; 

 2. Disable Minify CSS Files option from command line

Magento >= 2.2
php bin/magento config:set dev/css/minify_files 0
php bin/magento cache:clean
Magento < 2.2
mysql --host= --user= --password= -D  -e "\ UPDATE core_config_data SET value = 0 WHERE 'dev/css/minify_files' = path; \";
php bin/magento cache:clean

3. Disable Merge CSS Files option from command line

 

php bin/magento config:set dev/css/merge_css_files 0
php bin/magento cache:clean
Magento < 2.2
mysql --host= --user= --password= -D  -e "\ UPDATE core_config_data SET value = 0 WHERE 'dev/css/merge_css_files' = path; \";
php bin/magento cache:clean

How to install theme in Magento 2

Download the theme and copy the app folder and paste it in your magento root directory

  1. Open Shell, in the root directory, enter php bin/magento  setup:upgrade . This will upgrade all the magento files.

2. Also, Clear cache ,type  php bin/magento  cache:clean . 

3. Now force deploy the static files by typing  php bin/magento  setup:static-content:deploy -f . It will take a while.

4. Now, Go in the dashboard and select content->configuration.

5. Select the option with default store view and edit.

6. Select your theme from the options and click save and edit. If some problem arises, go to the vendor/magento/module-email/Model/AbstractTemplate.php and comment the “if”  condition of “setForcedArea” function, aprox. at line 535.

7. After changing theme successfully, clear the cache as done before.

Voila, the theme is changed!!

 

 

Magento File Directory

/app
The etc folder contains the di.xml configuration file
The di.xml file contains class mapping and interface preferences
Contains the design folder for themes.

/bin
The directory contains the magento file or a Command Line Interface script

/dev
Magento 2 testing and development tools are placed in this folder.

/lib
The internal and web subfolders are located here
The internal directory contains server-side libraries and several font files;
The web subfolder contains client-side libraries, jquery, modernizr, requirejs, prototype, scriptaculous

/pub
It should be set as your web root on the server
It contains all the publicly accessible files
index.php controller is also contained in the folder
pub also contains the error pages that will be displayed if your site is showing a 404 or 503 or another report page
Image uploads and imports will appear somewhere within pub/media.

/var
caches and generated classes are all located in the folder
the var folder includes cache, composer_home, generation, log, di, view_preprocessed, page_cache files

/vendor
the folder includes various packages that have been defined under composer.json
Marketplace modules and the core modules are all under vendor.
If you download something from the new Magento Marketplace.

[SOLVED] Magento 2 Error: A technical problem with the server created an error. Try again to continue what you were doing. If the problem persists, try again later.

Sometimes debugging on Magento can be annoying, specially if something just arises out of the blue, not while you are actually writing a code.

Magento Error - Technical Problem with server
If Problem Persists, try again later

This is something that occurs on Magento admin panel. As of now, it can be one out of 3 reasons, let us discuss them:

1. Admin Password

Try to change admin password, log out and log in again. This should fix it.

2. Some un compiled code or pending schema change

upgrade, compile, deploy, reindex, clean cache

rm -rf generated/* var/cache/* var/view_preprocessed/*
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento setup:static-content:deploy -f
php bin/magento indexer:reindex
php bin/magento cache:clean

3. Check for some extra output in the php files


See Line 41
One of the developers left this in index.php and this was just messing with the api calls from admin panel because it was corrupting json output.

Magento 2 : How to fix tablespace for table exists | base table or view already exists

Fixing General error: 1813 Tablespace for table xx Please DISCARD the tablespace before IMPORT and SQLSTATE[42S01]: Base table or view already exists

Recently I encountered this error while upgrading an extension on Magento 2.2.2
The error was

SQLSTATE[HY000]: General error: 1813 Tablespace for table '`jack_db1`.`jill_table`' exists. Please DISCARD the tablespace before IMPORT., query was: CREATE TABLE IF NOT EXISTS `jill_table`

When I tried to fix the above error, I received:

SQLSTATE[42S01]: Base table or view already exists: 1050 Table '`jack_db1`.`jill_table`' already exists, query was: CREATE TABLE IF NOT EXISTS `jill_table`

So here is how to fix it:

Important Note: Replace jack_db1 with your database Name and jill_table with your table Name

PART 1 – Fixing MySQL backend

First log in to server and check the mysql data folder (mine was /var/lib/mysql/jack_db1)
say your database name is jack_db1 and your table is jill_table
so issue

ls /var/lib/mysql/jack_db1

You will find .frm and .idb files
Note that these files are present as a couple (each frm file has a corresponding idb file)
But the table that is causing you issues will have one of them missing.
.frm was missing in my case, so copy any other frm file and name it as jill_table.frm (i copied wishlist.frm using following command )
cd /var/lib/mysql/jack_db1;cp wishlist.frm jill_table.frm
use the following command to fix ownership

chown -R mysql:mysql /var/lib/mysql/jack_db1/*

Part 2: Fixing Magento Backend

Login to phpMyAdmin if available or use MySQL CLI to perform the following actions.
In the table setup_module of your magento installation
Delete the row which is related to your corrupted extension

DELETE FROM `setup_module` WHERE module="Custom_Module"

you can also do the same from phpMyAdmin

Now delete the table which was causing the issue, (you would not be able to view the table in phpmMyAdmin)
Just run the following query in SQL

DROP TABLE IF EXISTS `jill_table`;

Part 3: Upgrade the Magento using php cli

run the following commands:

rm -rf var/cache/*
rm -rf generated/*
php bin/magento module:enable Custom_Module --clear-static-content
php bin/magento setup:upgrade
#  Hopefully this time you wont see any errors, proceed as usual if upgrade command worked.
#  run
php bin/magento setup:di:compile
#  and 
php bin/magento setup:static-content:deploy -f

Good Luck with Magento 🙂

Magento 2 how to show configurable options stock availability on product page

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) {
displayPrices.amount = newPrices.amount - displayPrices.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;
});

Magento 2 temporary fix for Alphabetical sorting of Configurable product options

Just re-write the file vendor/magento/module-configurable-product/Model/ConfigurableAttributeData.php

<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\ConfigurableProduct\Model;
use Magento\Catalog\Model\Product;
use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute;
/**
* Class ConfigurableAttributeData
* @api
* @since 100.0.2
*/
class ConfigurableAttributeData
{
/**
* Get product attributes
*
* @param Product $product
* @param array $options
* @return array
*/
public function getAttributesData(Product $product, array $options = [])
{
$defaultValues = [];
$attributes = [];
foreach ($product->getTypeInstance()->getConfigurableAttributes($product) 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,
];
}
public function labelsort($a,$b){ return strcmp($a['label'], $b['label']);}
/**
* @param Attribute $attribute
* @param array $config
* @return array
*/
protected function getAttributeOptionsData($attribute, $config)
{
$attributeOptionsData = [];
foreach ($attribute->getOptions() as $attributeOption) {
$optionId = $attributeOption['value_index'];
$attributeOptionsData[] = [
'id' => $optionId,
'label' => $attributeOption['label'],
'products' => isset($config[$attribute->getAttributeId()][$optionId])
? $config[$attribute->getAttributeId()][$optionId]
: [],
];
}
if(count($attributeOptionsData) > 1){usort($attributeOptionsData,array($this,'labelsort'));}
return $attributeOptionsData;
}
/**
* @param int $attributeId
* @param Product $product
* @return mixed|null
*/
protected function getAttributeConfigValue($attributeId, $product)
{
return $product->hasPreconfiguredValues()
? $product->getPreconfiguredValues()->getData('super_attribute/' . $attributeId)
: null;
}
}

How to update Magento community edition using CLI

composer require magento/product-community-edition 2.2.6 --no-update
composer update
rm -rf var/di var/generation var/cache var/view_preprocessed generated/*
php bin/magento cache:clean
php bin/magento cache:flush
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento indexer:reindex
php bin/magento deploy:mode:set production

if you receive any errors post installation, try this hard fix

rm -rf app/code/Magento/ var/* vendor/*
chmod 777 -R *
composer update && composer install
php -f bin/magento setup:static-content:deploy
find . -type d -exec chmod 775 {}
find . -type f -exec chmod 660 {}
chmod u+x bin/magento
bin/magento maintenance:disable
bin/magento cache:clean

How to add custom attributes in Magento 2 product CSV export

Core Method is to edit the file
/vendor/magento/module-catalog-import-export/Model/Export/Product.php

and Edit the variable $_exportMainAttrCodes

Add your attributes in the array like “custom1″,”custom2”, etc and save the file.

Next time you export the csv, you will see new columns in the file.