diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml
index baed8c1ce04de..71452a2d65e97 100644
--- a/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml
+++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml
@@ -33,7 +33,7 @@
= $block->getChildHtml('', true) ?>
diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml
index 038bea86e7d4e..22860418b157d 100644
--- a/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml
+++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml
@@ -21,17 +21,17 @@
$label = $block->getChildData($alias, 'title');
?>
+
= /* @escapeNotVerified */ $html ?>
diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml
index a2b91a5eeb99f..40f86c7e68d6c 100644
--- a/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml
+++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml
@@ -14,7 +14,7 @@
-getProduct()->getFinalPrice()):?>
+getProduct()->getPriceInfo()->getPrice(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE)->getAmount()):?>
= $block->getChildHtml('meta.currency') ?>
diff --git a/app/code/Magento/Catalog/view/frontend/web/js/validate-product.js b/app/code/Magento/Catalog/view/frontend/web/js/validate-product.js
index c0637cb672dc6..755e777a01f77 100644
--- a/app/code/Magento/Catalog/view/frontend/web/js/validate-product.js
+++ b/app/code/Magento/Catalog/view/frontend/web/js/validate-product.js
@@ -13,7 +13,8 @@ define([
$.widget('mage.productValidate', {
options: {
bindSubmit: false,
- radioCheckboxClosest: '.nested'
+ radioCheckboxClosest: '.nested',
+ addToCartButtonSelector: '.action.tocart'
},
/**
@@ -41,6 +42,7 @@ define([
return false;
}
});
+ $(this.options.addToCartButtonSelector).attr('disabled', false);
}
});
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
index 3578123e94dc1..9cb6d4bd2390d 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
@@ -3,12 +3,14 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\CatalogImportExport\Model\Import;
use Magento\Catalog\Model\Config as CatalogConfig;
use Magento\Catalog\Model\Product\Visibility;
use Magento\CatalogImportExport\Model\Import\Product\MediaGalleryProcessor;
use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface as ValidatorInterface;
+use Magento\CatalogImportExport\Model\StockItemImporterInterface;
use Magento\CatalogInventory\Api\Data\StockItemInterface;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Exception\LocalizedException;
@@ -638,6 +640,13 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
*/
private $_logger;
+ /**
+ * "Duplicate multiselect values" error array key
+ *
+ * @var string
+ */
+ private static $errorDuplicateMultiselectValues = 'duplicatedMultiselectValues';
+
/**
* {@inheritdoc}
*/
@@ -767,7 +776,6 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
* @param CatalogConfig $catalogConfig
* @param MediaGalleryProcessor $mediaProcessor
* @param ProductRepositoryInterface|null $productRepository
- * @throws \Magento\Framework\Exception\LocalizedException
*
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
@@ -857,11 +865,8 @@ public function __construct(
$string,
$errorAggregator
);
- $this->_optionEntity = isset(
- $data['option_entity']
- ) ? $data['option_entity'] : $optionFactory->create(
- ['data' => ['product_entity' => $this]]
- );
+ $this->_optionEntity = $data['option_entity']
+ ?? $optionFactory->create(['data' => ['product_entity' => $this]]);
$this->_initAttributeSets()
->_initTypeModels()
@@ -869,6 +874,9 @@ public function __construct(
$this->validator->init($this);
$this->productRepository = $productRepository ?? \Magento\Framework\App\ObjectManager::getInstance()
->get(ProductRepositoryInterface::class);
+
+ $errorMessageText = __('Value for multiselect attribute %s contains duplicated values');
+ $this->_messageTemplates[self::$errorDuplicateMultiselectValues] = $errorMessageText;
}
/**
@@ -884,7 +892,7 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData, $
{
if (!$this->validator->isAttributeValid($attrCode, $attrParams, $rowData)) {
foreach ($this->validator->getMessages() as $message) {
- $this->addRowError($message, $rowNum, $attrCode);
+ $this->skipRow($rowNum, $message, ProcessingError::ERROR_LEVEL_NOT_CRITICAL, $attrCode);
}
return false;
}
@@ -1592,7 +1600,7 @@ protected function _saveProducts()
if (!empty($rowData[self::URL_KEY])) {
// If url_key column and its value were in the CSV file
$rowData[self::URL_KEY] = $urlKey;
- } else if ($this->isNeedToChangeUrlKey($rowData)) {
+ } elseif ($this->isNeedToChangeUrlKey($rowData)) {
// If url_key column was empty or even not declared in the CSV file but by the rules it is need to
// be setteed. In case when url_key is generating from name column we have to ensure that the bunch
// of products will pass for the event with url_key column.
@@ -1604,7 +1612,9 @@ protected function _saveProducts()
if (null === $rowSku) {
$this->getErrorAggregator()->addRowToSkip($rowNum);
continue;
- } elseif (self::SCOPE_STORE == $rowScope) {
+ }
+
+ if (self::SCOPE_STORE == $rowScope) {
// set necessary data from SCOPE_DEFAULT row
$rowData[self::COL_TYPE] = $this->skuProcessor->getNewSku($rowSku)['type_id'];
$rowData['attribute_set_id'] = $this->skuProcessor->getNewSku($rowSku)['attr_set_id'];
@@ -1740,13 +1750,7 @@ protected function _saveProducts()
$uploadedImages[$columnImage] = $uploadedFile;
} else {
unset($rowData[$column]);
- $this->addRowError(
- ValidatorInterface::ERROR_MEDIA_URL_NOT_ACCESSIBLE,
- $rowNum,
- null,
- null,
- ProcessingError::ERROR_LEVEL_NOT_CRITICAL
- );
+ $this->skipRow($rowNum, ValidatorInterface::ERROR_MEDIA_URL_NOT_ACCESSIBLE);
}
} else {
$uploadedFile = $uploadedImages[$columnImage];
@@ -2380,32 +2384,35 @@ public function validateRow(array $rowData, $rowNum)
// BEHAVIOR_DELETE and BEHAVIOR_REPLACE use specific validation logic
if (Import::BEHAVIOR_REPLACE == $this->getBehavior()) {
if (self::SCOPE_DEFAULT == $rowScope && !$this->isSkuExist($sku)) {
- $this->addRowError(ValidatorInterface::ERROR_SKU_NOT_FOUND_FOR_DELETE, $rowNum);
+ $this->skipRow($rowNum, ValidatorInterface::ERROR_SKU_NOT_FOUND_FOR_DELETE);
return false;
}
}
if (Import::BEHAVIOR_DELETE == $this->getBehavior()) {
if (self::SCOPE_DEFAULT == $rowScope && !$this->isSkuExist($sku)) {
- $this->addRowError(ValidatorInterface::ERROR_SKU_NOT_FOUND_FOR_DELETE, $rowNum);
+ $this->skipRow($rowNum, ValidatorInterface::ERROR_SKU_NOT_FOUND_FOR_DELETE);
return false;
}
return true;
}
+ // if product doesn't exist, need to throw critical error else all errors should be not critical.
+ $errorLevel = $this->getValidationErrorLevel($sku);
+
if (!$this->validator->isValid($rowData)) {
foreach ($this->validator->getMessages() as $message) {
- $this->addRowError($message, $rowNum, $this->validator->getInvalidAttribute());
+ $this->skipRow($rowNum, $message, $errorLevel, $this->validator->getInvalidAttribute());
}
}
if (null === $sku) {
- $this->addRowError(ValidatorInterface::ERROR_SKU_IS_EMPTY, $rowNum);
+ $this->skipRow($rowNum, ValidatorInterface::ERROR_SKU_IS_EMPTY, $errorLevel);
} elseif (false === $sku) {
- $this->addRowError(ValidatorInterface::ERROR_ROW_IS_ORPHAN, $rowNum);
+ $this->skipRow($rowNum, ValidatorInterface::ERROR_ROW_IS_ORPHAN, $errorLevel);
} elseif (self::SCOPE_STORE == $rowScope
&& !$this->storeResolver->getStoreCodeToId($rowData[self::COL_STORE])
) {
- $this->addRowError(ValidatorInterface::ERROR_INVALID_STORE, $rowNum);
+ $this->skipRow($rowNum, ValidatorInterface::ERROR_INVALID_STORE, $errorLevel);
}
// SKU is specified, row is SCOPE_DEFAULT, new product block begins
@@ -2420,19 +2427,15 @@ public function validateRow(array $rowData, $rowNum)
$this->prepareNewSkuData($sku)
);
} else {
- $this->addRowError(ValidatorInterface::ERROR_TYPE_UNSUPPORTED, $rowNum);
+ $this->skipRow($rowNum, ValidatorInterface::ERROR_TYPE_UNSUPPORTED, $errorLevel);
}
} else {
// validate new product type and attribute set
- if (!isset($rowData[self::COL_TYPE]) || !isset($this->_productTypeModels[$rowData[self::COL_TYPE]])) {
- $this->addRowError(ValidatorInterface::ERROR_INVALID_TYPE, $rowNum);
- } elseif (!isset(
- $rowData[self::COL_ATTR_SET]
- ) || !isset(
- $this->_attrSetNameToId[$rowData[self::COL_ATTR_SET]]
- )
+ if (!isset($rowData[self::COL_TYPE], $this->_productTypeModels[$rowData[self::COL_TYPE]])) {
+ $this->skipRow($rowNum, ValidatorInterface::ERROR_INVALID_TYPE, $errorLevel);
+ } elseif (!isset($rowData[self::COL_ATTR_SET], $this->_attrSetNameToId[$rowData[self::COL_ATTR_SET]])
) {
- $this->addRowError(ValidatorInterface::ERROR_INVALID_ATTR_SET, $rowNum);
+ $this->skipRow($rowNum, ValidatorInterface::ERROR_INVALID_ATTR_SET, $errorLevel);
} elseif (is_null($this->skuProcessor->getNewSku($sku))) {
$this->skuProcessor->addNewSku(
$sku,
@@ -2488,8 +2491,11 @@ public function validateRow(array $rowData, $rowNum)
ValidatorInterface::ERROR_DUPLICATE_URL_KEY,
$rowNum,
$rowData[self::COL_NAME],
- $message
- );
+ $message,
+ $errorLevel
+ )
+ ->getErrorAggregator()
+ ->addRowToSkip($rowNum);
}
}
}
@@ -2499,9 +2505,10 @@ public function validateRow(array $rowData, $rowNum)
$newFromTimestamp = strtotime($this->dateTime->formatDate($rowData[self::COL_NEW_FROM_DATE], false));
$newToTimestamp = strtotime($this->dateTime->formatDate($rowData[self::COL_NEW_TO_DATE], false));
if ($newFromTimestamp > $newToTimestamp) {
- $this->addRowError(
- ValidatorInterface::ERROR_NEW_TO_DATE,
+ $this->skipRow(
$rowNum,
+ ValidatorInterface::ERROR_NEW_TO_DATE,
+ $errorLevel,
$rowData[self::COL_NEW_TO_DATE]
);
}
@@ -3029,4 +3036,39 @@ private function retrieveProductBySku(string $sku)
return $product;
}
+
+ /**
+ * Add row as skipped
+ *
+ * @param int $rowNum
+ * @param string $errorCode Error code or simply column name
+ * @param string $errorLevel error level
+ * @param string|null $colName optional column name
+ * @return $this
+ */
+ private function skipRow(
+ int $rowNum,
+ string $errorCode,
+ string $errorLevel = ProcessingError::ERROR_LEVEL_NOT_CRITICAL,
+ $colName = null
+ ): self {
+ $this->addRowError($errorCode, $rowNum, $colName, null, $errorLevel);
+ $this->getErrorAggregator()
+ ->addRowToSkip($rowNum);
+
+ return $this;
+ }
+
+ /**
+ * Returns errorLevel for validation
+ *
+ * @param string|bool|null $sku
+ * @return string
+ */
+ private function getValidationErrorLevel($sku): string
+ {
+ return (!$this->isSkuExist($sku) && Import::BEHAVIOR_REPLACE !== $this->getBehavior())
+ ? ProcessingError::ERROR_LEVEL_CRITICAL
+ : ProcessingError::ERROR_LEVEL_NOT_CRITICAL;
+ }
}
diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php
index 1cd19852f393c..3f72fcc39f548 100644
--- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php
+++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php
@@ -3,10 +3,14 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\CatalogImportExport\Test\Unit\Model\Import;
+use Magento\CatalogImportExport\Model\Import\Product;
+use Magento\CatalogImportExport\Model\Import\Product\ImageTypeProcessor;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\ImportExport\Model\Import;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
/**
* Class ProductTest
@@ -25,126 +29,126 @@ class ProductTest extends \Magento\ImportExport\Test\Unit\Model\Import\AbstractI
const ENTITY_ID = 13;
- /** @var \Magento\Framework\DB\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\Framework\DB\Adapter\AdapterInterface| MockObject */
protected $_connection;
- /** @var \Magento\Framework\Json\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\Framework\Json\Helper\Data| MockObject */
protected $jsonHelper;
- /** @var \Magento\ImportExport\Model\ResourceModel\Import\Data|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\ImportExport\Model\ResourceModel\Import\Data| MockObject */
protected $_dataSourceModel;
- /** @var \Magento\Framework\App\ResourceConnection|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\Framework\App\ResourceConnection| MockObject */
protected $resource;
- /** @var \Magento\ImportExport\Model\ResourceModel\Helper|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\ImportExport\Model\ResourceModel\Helper| MockObject */
protected $_resourceHelper;
- /** @var \Magento\Framework\Stdlib\StringUtils|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\Framework\Stdlib\StringUtils|MockObject */
protected $string;
- /** @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\Framework\Event\ManagerInterface|MockObject */
protected $_eventManager;
- /** @var \Magento\CatalogInventory\Api\StockRegistryInterface|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\CatalogInventory\Api\StockRegistryInterface|MockObject */
protected $stockRegistry;
- /** @var \Magento\CatalogImportExport\Model\Import\Product\OptionFactory|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\CatalogImportExport\Model\Import\Product\OptionFactory|MockObject */
protected $optionFactory;
- /** @var \Magento\CatalogInventory\Api\StockConfigurationInterface|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\CatalogInventory\Api\StockConfigurationInterface|MockObject */
protected $stockConfiguration;
- /** @var \Magento\CatalogInventory\Model\Spi\StockStateProviderInterface|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\CatalogInventory\Model\Spi\StockStateProviderInterface|MockObject */
protected $stockStateProvider;
- /** @var \Magento\CatalogImportExport\Model\Import\Product\Option|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\CatalogImportExport\Model\Import\Product\Option|MockObject */
protected $optionEntity;
- /** @var \Magento\Framework\Stdlib\DateTime|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\Framework\Stdlib\DateTime|MockObject */
protected $dateTime;
/** @var array */
protected $data;
- /** @var \Magento\ImportExport\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\ImportExport\Helper\Data|MockObject */
protected $importExportData;
- /** @var \Magento\ImportExport\Model\ResourceModel\Import\Data|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\ImportExport\Model\ResourceModel\Import\Data|MockObject */
protected $importData;
- /** @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\Eav\Model\Config|MockObject */
protected $config;
- /** @var \Magento\ImportExport\Model\ResourceModel\Helper|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\ImportExport\Model\ResourceModel\Helper|MockObject */
protected $resourceHelper;
- /** @var \Magento\Catalog\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\Catalog\Helper\Data|MockObject */
protected $_catalogData;
- /** @var \Magento\ImportExport\Model\Import\Config|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\ImportExport\Model\Import\Config|MockObject */
protected $_importConfig;
- /** @var \PHPUnit_Framework_MockObject_MockObject */
+ /** @var MockObject */
protected $_resourceFactory;
// @codingStandardsIgnoreStart
- /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory|MockObject */
protected $_setColFactory;
- /** @var \Magento\CatalogImportExport\Model\Import\Product\Type\Factory|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\CatalogImportExport\Model\Import\Product\Type\Factory|MockObject */
protected $_productTypeFactory;
- /** @var \Magento\Catalog\Model\ResourceModel\Product\LinkFactory|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\Catalog\Model\ResourceModel\Product\LinkFactory|MockObject */
protected $_linkFactory;
- /** @var \Magento\CatalogImportExport\Model\Import\Proxy\ProductFactory|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\CatalogImportExport\Model\Import\Proxy\ProductFactory|MockObject */
protected $_proxyProdFactory;
- /** @var \Magento\CatalogImportExport\Model\Import\UploaderFactory|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\CatalogImportExport\Model\Import\UploaderFactory|MockObject */
protected $_uploaderFactory;
- /** @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\Framework\Filesystem|MockObject */
protected $_filesystem;
- /** @var \Magento\Framework\Filesystem\Directory\WriteInterface|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\Framework\Filesystem\Directory\WriteInterface|MockObject */
protected $_mediaDirectory;
- /** @var \Magento\CatalogInventory\Model\ResourceModel\Stock\ItemFactory|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\CatalogInventory\Model\ResourceModel\Stock\ItemFactory|MockObject */
protected $_stockResItemFac;
- /** @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface|MockObject */
protected $_localeDate;
- /** @var \Magento\Framework\Indexer\IndexerRegistry|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\Framework\Indexer\IndexerRegistry|MockObject */
protected $indexerRegistry;
- /** @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Psr\Log\LoggerInterface|MockObject */
protected $_logger;
- /** @var \Magento\CatalogImportExport\Model\Import\Product\StoreResolver|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\CatalogImportExport\Model\Import\Product\StoreResolver|MockObject */
protected $storeResolver;
- /** @var \Magento\CatalogImportExport\Model\Import\Product\SkuProcessor|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\CatalogImportExport\Model\Import\Product\SkuProcessor|MockObject */
protected $skuProcessor;
- /** @var \Magento\CatalogImportExport\Model\Import\Product\CategoryProcessor|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\CatalogImportExport\Model\Import\Product\CategoryProcessor|MockObject */
protected $categoryProcessor;
- /** @var \Magento\CatalogImportExport\Model\Import\Product\Validator|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\CatalogImportExport\Model\Import\Product\Validator|MockObject */
protected $validator;
- /** @var \Magento\Framework\Model\ResourceModel\Db\ObjectRelationProcessor|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\Framework\Model\ResourceModel\Db\ObjectRelationProcessor|MockObject */
protected $objectRelationProcessor;
- /** @var \Magento\Framework\Model\ResourceModel\Db\TransactionManagerInterface|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\Framework\Model\ResourceModel\Db\TransactionManagerInterface|MockObject */
protected $transactionManager;
- /** @var \Magento\CatalogImportExport\Model\Import\Product\TaxClassProcessor|\PHPUnit_Framework_MockObject_MockObject */
+ /** @var \Magento\CatalogImportExport\Model\Import\Product\TaxClassProcessor|MockObject */
// @codingStandardsIgnoreEnd
protected $taxClassProcessor;
- /** @var \Magento\CatalogImportExport\Model\Import\Product */
+ /** @var Product */
protected $importProduct;
/**
@@ -152,10 +156,10 @@ class ProductTest extends \Magento\ImportExport\Test\Unit\Model\Import\AbstractI
*/
protected $errorAggregator;
- /** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject*/
+ /** @var \Magento\Framework\App\Config\ScopeConfigInterface|MockObject */
protected $scopeConfig;
- /** @var \Magento\Catalog\Model\Product\Url|\PHPUnit_Framework_MockObject_MockObject*/
+ /** @var \Magento\Catalog\Model\Product\Url|MockObject */
protected $productUrl;
/**
@@ -334,7 +338,7 @@ protected function setUp()
$objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
$this->importProduct = $objectManager->getObject(
- \Magento\CatalogImportExport\Model\Import\Product::class,
+ Product::class,
[
'jsonHelper' => $this->jsonHelper,
'importExportData' => $this->importExportData,
@@ -375,7 +379,7 @@ protected function setUp()
'data' => $this->data
]
);
- $reflection = new \ReflectionClass(\Magento\CatalogImportExport\Model\Import\Product::class);
+ $reflection = new \ReflectionClass(Product::class);
$reflectionProperty = $reflection->getProperty('metadataPool');
$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($this->importProduct, $metadataPoolMock);
@@ -584,7 +588,7 @@ public function testGetMultipleValueSeparatorFromParameters()
public function testDeleteProductsForReplacement()
{
- $importProduct = $this->getMockBuilder(\Magento\CatalogImportExport\Model\Import\Product::class)
+ $importProduct = $this->getMockBuilder(Product::class)
->disableOriginalConstructor()
->setMethods([
'setParameters', '_deleteProducts'
@@ -650,7 +654,7 @@ public function testValidateRowIsAlreadyValidated()
*/
public function testValidateRow($rowScope, $oldSku, $expectedResult, $behaviour = Import::BEHAVIOR_DELETE)
{
- $importProduct = $this->getMockBuilder(\Magento\CatalogImportExport\Model\Import\Product::class)
+ $importProduct = $this->getMockBuilder(Product::class)
->disableOriginalConstructor()
->setMethods(['getBehavior', 'getRowScope', 'getErrorAggregator'])
->getMock();
@@ -662,7 +666,7 @@ public function testValidateRow($rowScope, $oldSku, $expectedResult, $behaviour
->method('getErrorAggregator')
->willReturn($this->getErrorAggregatorObject());
$importProduct->expects($this->once())->method('getRowScope')->willReturn($rowScope);
- $skuKey = \Magento\CatalogImportExport\Model\Import\Product::COL_SKU;
+ $skuKey = Product::COL_SKU;
$rowData = [
$skuKey => 'sku',
];
@@ -674,18 +678,22 @@ public function testValidateRow($rowScope, $oldSku, $expectedResult, $behaviour
public function testValidateRowDeleteBehaviourAddRowErrorCall()
{
- $importProduct = $this->getMockBuilder(\Magento\CatalogImportExport\Model\Import\Product::class)
+ $importProduct = $this->getMockBuilder(Product::class)
->disableOriginalConstructor()
- ->setMethods(['getBehavior', 'getRowScope', 'addRowError'])
+ ->setMethods(['getBehavior', 'getRowScope', 'addRowError', 'getErrorAggregator'])
->getMock();
$importProduct->expects($this->exactly(2))->method('getBehavior')
->willReturn(\Magento\ImportExport\Model\Import::BEHAVIOR_DELETE);
$importProduct->expects($this->once())->method('getRowScope')
- ->willReturn(\Magento\CatalogImportExport\Model\Import\Product::SCOPE_DEFAULT);
+ ->willReturn(Product::SCOPE_DEFAULT);
$importProduct->expects($this->once())->method('addRowError');
+ $importProduct->method('getErrorAggregator')
+ ->willReturn(
+ $this->getErrorAggregatorObject(['addRowToSkip'])
+ );
$rowData = [
- \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => 'sku',
+ Product::COL_SKU => 'sku',
];
$importProduct->validateRow($rowData, 0);
@@ -696,7 +704,7 @@ public function testValidateRowValidatorCheck()
$messages = ['validator message'];
$this->validator->expects($this->once())->method('getMessages')->willReturn($messages);
$rowData = [
- \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => 'sku',
+ Product::COL_SKU => 'sku',
];
$rowNum = 0;
$this->importProduct->validateRow($rowData, $rowNum);
@@ -798,7 +806,7 @@ public function getStoreIdByCodeDataProvider()
return [
[
'$storeCode' => null,
- '$expectedResult' => \Magento\CatalogImportExport\Model\Import\Product::SCOPE_DEFAULT,
+ '$expectedResult' => Product::SCOPE_DEFAULT,
],
[
'$storeCode' => 'value',
@@ -813,17 +821,17 @@ public function getStoreIdByCodeDataProvider()
public function testValidateRowCheckSpecifiedSku($sku, $expectedError)
{
$importProduct = $this->createModelMockWithErrorAggregator(
- [ 'addRowError', 'getOptionEntity', 'getRowScope'],
+ ['addRowError', 'getOptionEntity', 'getRowScope'],
['isRowInvalid' => true]
);
$rowNum = 0;
$rowData = [
- \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $sku,
- \Magento\CatalogImportExport\Model\Import\Product::COL_STORE => '',
+ Product::COL_SKU => $sku,
+ Product::COL_STORE => '',
];
- $this->storeResolver->expects($this->any())->method('getStoreCodeToId')->willReturn(null);
+ $this->storeResolver->method('getStoreCodeToId')->willReturn(null);
$this->setPropertyValue($importProduct, 'storeResolver', $this->storeResolver);
$this->setPropertyValue($importProduct, 'skuProcessor', $this->skuProcessor);
@@ -832,7 +840,7 @@ public function testValidateRowCheckSpecifiedSku($sku, $expectedError)
$importProduct
->expects($this->once())
->method('getRowScope')
- ->willReturn(\Magento\CatalogImportExport\Model\Import\Product::SCOPE_STORE);
+ ->willReturn(Product::SCOPE_STORE);
$importProduct->expects($this->at(1))->method('addRowError')->with($expectedError, $rowNum)->willReturn(null);
$importProduct->validateRow($rowData, $rowNum);
@@ -846,7 +854,7 @@ public function testValidateRowProcessEntityIncrement()
$errorAggregator->method('isRowInvalid')->willReturn(true);
$this->setPropertyValue($this->importProduct, '_processedEntitiesCount', $count);
$this->setPropertyValue($this->importProduct, 'errorAggregator', $errorAggregator);
- $rowData = [\Magento\CatalogImportExport\Model\Import\Product::COL_SKU => false];
+ $rowData = [Product::COL_SKU => false];
//suppress validator
$this->_setValidatorMockInImportProduct($this->importProduct);
$this->importProduct->validateRow($rowData, $rowNum);
@@ -856,14 +864,14 @@ public function testValidateRowProcessEntityIncrement()
public function testValidateRowValidateExistingProductTypeAddNewSku()
{
$importProduct = $this->createModelMockWithErrorAggregator(
- [ 'addRowError', 'getOptionEntity'],
+ ['addRowError', 'getOptionEntity'],
['isRowInvalid' => true]
);
$sku = 'sku';
$rowNum = 0;
$rowData = [
- \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $sku,
+ Product::COL_SKU => $sku,
];
$oldSku = [
$sku => [
@@ -886,7 +894,7 @@ public function testValidateRowValidateExistingProductTypeAddNewSku()
$this->setPropertyValue($importProduct, '_oldSku', $oldSku);
$expectedData = [
- 'entity_id' => $oldSku[$sku]['entity_id'], //entity_id_val
+ 'entity_id' => $oldSku[$sku]['entity_id'], //entity_id_val
'type_id' => $oldSku[$sku]['type_id'],// type_id_val
'attr_set_id' => $oldSku[$sku]['attr_set_id'], //attr_set_id_val
'attr_set_code' => $_attrSetIdToName[$oldSku[$sku]['attr_set_id']],//attr_set_id_val
@@ -904,7 +912,7 @@ public function testValidateRowValidateExistingProductTypeAddErrorRowCall()
$sku = 'sku';
$rowNum = 0;
$rowData = [
- \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $sku,
+ Product::COL_SKU => $sku,
];
$oldSku = [
$sku => [
@@ -929,6 +937,11 @@ public function testValidateRowValidateExistingProductTypeAddErrorRowCall()
/**
* @dataProvider validateRowValidateNewProductTypeAddRowErrorCallDataProvider
+ * @param string $colType
+ * @param string $productTypeModelsColType
+ * @param string $colAttrSet
+ * @param string $attrSetNameToIdColAttrSet
+ * @param string $error
*/
public function testValidateRowValidateNewProductTypeAddRowErrorCall(
$colType,
@@ -940,15 +953,15 @@ public function testValidateRowValidateNewProductTypeAddRowErrorCall(
$sku = 'sku';
$rowNum = 0;
$rowData = [
- \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $sku,
- \Magento\CatalogImportExport\Model\Import\Product::COL_TYPE => $colType,
- \Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET => $colAttrSet,
+ Product::COL_SKU => $sku,
+ Product::COL_TYPE => $colType,
+ Product::COL_ATTR_SET => $colAttrSet,
];
$_attrSetNameToId = [
- $rowData[\Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET] => $attrSetNameToIdColAttrSet,
+ $rowData[Product::COL_ATTR_SET] => $attrSetNameToIdColAttrSet,
];
$_productTypeModels = [
- $rowData[\Magento\CatalogImportExport\Model\Import\Product::COL_TYPE] => $productTypeModelsColType,
+ $rowData[Product::COL_TYPE] => $productTypeModelsColType,
];
$oldSku = [
$sku => null,
@@ -976,29 +989,25 @@ public function testValidateRowValidateNewProductTypeGetNewSkuCall()
$sku = 'sku';
$rowNum = 0;
$rowData = [
- \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $sku,
- \Magento\CatalogImportExport\Model\Import\Product::COL_TYPE => 'value',
- \Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET => 'value',
+ Product::COL_SKU => $sku,
+ Product::COL_TYPE => 'value',
+ Product::COL_ATTR_SET => 'value',
];
$_productTypeModels = [
- $rowData[\Magento\CatalogImportExport\Model\Import\Product::COL_TYPE] => 'value',
+ $rowData[Product::COL_TYPE] => 'value',
];
$oldSku = [
$sku => null,
];
$_attrSetNameToId = [
- $rowData[\Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET] => 'attr_set_code_val'
+ $rowData[Product::COL_ATTR_SET] => 'attr_set_code_val',
];
$expectedData = [
'entity_id' => null,
- 'type_id' => $rowData[\Magento\CatalogImportExport\Model\Import\Product::COL_TYPE],//value
+ 'type_id' => $rowData[Product::COL_TYPE],
//attr_set_id_val
- 'attr_set_id' => $_attrSetNameToId[
- $rowData[
- \Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET
- ]
- ],
- 'attr_set_code' => $rowData[\Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET],//value
+ 'attr_set_id' => $_attrSetNameToId[$rowData[Product::COL_ATTR_SET]],
+ 'attr_set_code' => $rowData[Product::COL_ATTR_SET],
'row_id' => null
];
$importProduct = $this->createModelMockWithErrorAggregator(
@@ -1034,8 +1043,8 @@ public function testValidateRowSetAttributeSetCodeIntoRowData()
$sku = 'sku';
$rowNum = 0;
$rowData = [
- \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $sku,
- \Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET => 'col_attr_set_val',
+ Product::COL_SKU => $sku,
+ Product::COL_ATTR_SET => 'col_attr_set_val',
];
$expectedAttrSetCode = 'new_attr_set_code';
$newSku = [
@@ -1043,8 +1052,8 @@ public function testValidateRowSetAttributeSetCodeIntoRowData()
'type_id' => 'new_type_id_val',
];
$expectedRowData = [
- \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $sku,
- \Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET => $newSku['attr_set_code'],
+ Product::COL_SKU => $sku,
+ Product::COL_ATTR_SET => $newSku['attr_set_code'],
];
$oldSku = [
$sku => [
@@ -1078,8 +1087,8 @@ public function testValidateValidateOptionEntity()
$sku = 'sku';
$rowNum = 0;
$rowData = [
- \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $sku,
- \Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET => 'col_attr_set_val',
+ Product::COL_SKU => $sku,
+ Product::COL_ATTR_SET => 'col_attr_set_val',
];
$oldSku = [
$sku => [
@@ -1336,7 +1345,7 @@ public function validateRowDataProvider()
{
return [
[
- '$rowScope' => \Magento\CatalogImportExport\Model\Import\Product::SCOPE_DEFAULT,
+ '$rowScope' => Product::SCOPE_DEFAULT,
'$oldSku' => null,
'$expectedResult' => false,
],
@@ -1351,12 +1360,12 @@ public function validateRowDataProvider()
'$expectedResult' => true,
],
[
- '$rowScope' => \Magento\CatalogImportExport\Model\Import\Product::SCOPE_DEFAULT,
+ '$rowScope' => Product::SCOPE_DEFAULT,
'$oldSku' => true,
'$expectedResult' => true,
],
[
- '$rowScope' => \Magento\CatalogImportExport\Model\Import\Product::SCOPE_DEFAULT,
+ '$rowScope' => Product::SCOPE_DEFAULT,
'$oldSku' => null,
'$expectedResult' => false,
'$behaviour' => Import::BEHAVIOR_REPLACE
@@ -1377,7 +1386,7 @@ public function isAttributeValidAssertAttrValidDataProvider()
'$rowData' => [
'code' => str_repeat(
'a',
- \Magento\CatalogImportExport\Model\Import\Product::DB_MAX_VARCHAR_LENGTH - 1
+ Product::DB_MAX_VARCHAR_LENGTH - 1
),
],
],
@@ -1430,7 +1439,7 @@ public function isAttributeValidAssertAttrValidDataProvider()
'$rowData' => [
'code' => str_repeat(
'a',
- \Magento\CatalogImportExport\Model\Import\Product::DB_MAX_TEXT_LENGTH - 1
+ Product::DB_MAX_TEXT_LENGTH - 1
),
],
],
@@ -1450,7 +1459,7 @@ public function isAttributeValidAssertAttrInvalidDataProvider()
'$rowData' => [
'code' => str_repeat(
'a',
- \Magento\CatalogImportExport\Model\Import\Product::DB_MAX_VARCHAR_LENGTH + 1
+ Product::DB_MAX_VARCHAR_LENGTH + 1
),
],
],
@@ -1503,7 +1512,7 @@ public function isAttributeValidAssertAttrInvalidDataProvider()
'$rowData' => [
'code' => str_repeat(
'a',
- \Magento\CatalogImportExport\Model\Import\Product::DB_MAX_TEXT_LENGTH + 1
+ Product::DB_MAX_TEXT_LENGTH + 1
),
],
],
@@ -1515,8 +1524,8 @@ public function isAttributeValidAssertAttrInvalidDataProvider()
*/
public function getRowScopeDataProvider()
{
- $colSku = \Magento\CatalogImportExport\Model\Import\Product::COL_SKU;
- $colStore = \Magento\CatalogImportExport\Model\Import\Product::COL_STORE;
+ $colSku = Product::COL_SKU;
+ $colStore = Product::COL_STORE;
return [
[
@@ -1524,21 +1533,21 @@ public function getRowScopeDataProvider()
$colSku => null,
$colStore => 'store',
],
- '$expectedResult' => \Magento\CatalogImportExport\Model\Import\Product::SCOPE_STORE
+ '$expectedResult' => Product::SCOPE_STORE,
],
[
'$rowData' => [
$colSku => 'sku',
$colStore => null,
],
- '$expectedResult' => \Magento\CatalogImportExport\Model\Import\Product::SCOPE_DEFAULT
+ '$expectedResult' => Product::SCOPE_DEFAULT,
],
[
'$rowData' => [
$colSku => 'sku',
$colStore => 'store',
],
- '$expectedResult' => \Magento\CatalogImportExport\Model\Import\Product::SCOPE_STORE
+ '$expectedResult' => Product::SCOPE_STORE,
],
];
}
@@ -1615,9 +1624,9 @@ protected function overrideMethod(&$object, $methodName, array $parameters = [])
*
* @see _rewriteGetOptionEntityInImportProduct()
* @see _setValidatorMockInImportProduct()
- * @param \Magento\CatalogImportExport\Model\Import\Product
+ * @param Product
* Param should go with rewritten getOptionEntity method.
- * @return \Magento\CatalogImportExport\Model\Import\Product\Option|\PHPUnit_Framework_MockObject_MockObject
+ * @return \Magento\CatalogImportExport\Model\Import\Product\Option|MockObject
*/
private function _suppressValidateRowOptionValidatorInvalidRows($importProduct)
{
@@ -1633,8 +1642,8 @@ private function _suppressValidateRowOptionValidatorInvalidRows($importProduct)
* Used in group of validateRow method's tests.
* Set validator mock in importProduct, return true for isValid method.
*
- * @param \Magento\CatalogImportExport\Model\Import\Product
- * @return \Magento\CatalogImportExport\Model\Import\Product\Validator|\PHPUnit_Framework_MockObject_MockObject
+ * @param Product
+ * @return \Magento\CatalogImportExport\Model\Import\Product\Validator|MockObject
*/
private function _setValidatorMockInImportProduct($importProduct)
{
@@ -1648,9 +1657,9 @@ private function _setValidatorMockInImportProduct($importProduct)
* Used in group of validateRow method's tests.
* Make getOptionEntity return option mock.
*
- * @param \Magento\CatalogImportExport\Model\Import\Product
+ * @param Product
* Param should go with rewritten getOptionEntity method.
- * @return \Magento\CatalogImportExport\Model\Import\Product\Option|\PHPUnit_Framework_MockObject_MockObject
+ * @return \Magento\CatalogImportExport\Model\Import\Product\Option|MockObject
*/
private function _rewriteGetOptionEntityInImportProduct($importProduct)
{
@@ -1665,12 +1674,12 @@ private function _rewriteGetOptionEntityInImportProduct($importProduct)
/**
* @param array $methods
* @param array $errorAggregatorMethods
- * @return \PHPUnit_Framework_MockObject_MockObject
+ * @return MockObject
*/
protected function createModelMockWithErrorAggregator(array $methods = [], array $errorAggregatorMethods = [])
{
$methods[] = 'getErrorAggregator';
- $importProduct = $this->getMockBuilder(\Magento\CatalogImportExport\Model\Import\Product::class)
+ $importProduct = $this->getMockBuilder(Product::class)
->disableOriginalConstructor()
->setMethods($methods)
->getMock();
diff --git a/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/AddQuantityAndStockStatusFieldToCollection.php b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/AddQuantityAndStockStatusFieldToCollection.php
new file mode 100644
index 0000000000000..d66a783c6720d
--- /dev/null
+++ b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/AddQuantityAndStockStatusFieldToCollection.php
@@ -0,0 +1,32 @@
+joinField(
+ 'quantity_and_stock_status',
+ 'cataloginventory_stock_item',
+ 'is_in_stock',
+ 'product_id=entity_id',
+ '{{table}}.stock_id=1',
+ 'left'
+ );
+ }
+}
diff --git a/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml b/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml
index 803a6dae492a0..601f7ef61973b 100644
--- a/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml
+++ b/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml
@@ -23,6 +23,7 @@
- Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityFieldToCollection
+ - Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityAndStockStatusFieldToCollection
- Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityFilterToCollection
diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml
index 535e91ba30f52..8c47865da6aa7 100644
--- a/app/code/Magento/CatalogInventory/etc/di.xml
+++ b/app/code/Magento/CatalogInventory/etc/di.xml
@@ -44,7 +44,7 @@
- Magento\CatalogInventory\Model\ResourceModel\Stock\Proxy
+ Magento\CatalogInventory\Model\ResourceModel\Stock\Item\Proxy
diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php
index fc2056e83ec70..12334a2a773cb 100644
--- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php
+++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php
@@ -13,7 +13,12 @@
use Magento\Store\Model\Store;
use Magento\UrlRewrite\Model\UrlPersistInterface;
use Magento\UrlRewrite\Service\V1\Data\UrlRewrite;
+use Magento\Framework\Exception\NoSuchEntityException;
+use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException;
+/**
+ * Observer to assign the products to website.
+ */
class ProductToWebsiteChangeObserver implements ObserverInterface
{
/**
@@ -58,23 +63,30 @@ public function __construct(
* Generate urls for UrlRewrite and save it in storage
*
* @param \Magento\Framework\Event\Observer $observer
+ * @throws NoSuchEntityException
+ * @throws UrlAlreadyExistsException
* @return void
*/
public function execute(\Magento\Framework\Event\Observer $observer)
{
foreach ($observer->getEvent()->getProducts() as $productId) {
+ $storeId = $this->request->getParam('store_id', Store::DEFAULT_STORE_ID);
+
$product = $this->productRepository->getById(
$productId,
false,
- $this->request->getParam('store_id', Store::DEFAULT_STORE_ID)
+ $storeId
);
- $this->urlPersist->deleteByData([
- UrlRewrite::ENTITY_ID => $product->getId(),
- UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE,
- ]);
- if ($product->getVisibility() != Visibility::VISIBILITY_NOT_VISIBLE) {
- $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product));
+ if (!empty($this->productUrlRewriteGenerator->generate($product))) {
+ $this->urlPersist->deleteByData([
+ UrlRewrite::ENTITY_ID => $product->getId(),
+ UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE,
+ UrlRewrite::STORE_ID => $storeId,
+ ]);
+ if ($product->getVisibility() != Visibility::VISIBILITY_NOT_VISIBLE) {
+ $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product));
+ }
}
}
}
diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml
new file mode 100644
index 0000000000000..d52395342c092
--- /dev/null
+++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductToWebsiteChangeObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductToWebsiteChangeObserverTest.php
new file mode 100644
index 0000000000000..f383c949b4295
--- /dev/null
+++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductToWebsiteChangeObserverTest.php
@@ -0,0 +1,193 @@
+productId = 3;
+
+ $this->urlPersist = $this->getMockBuilder(UrlPersistInterface::class)
+ ->setMethods(['deleteByData', 'replace'])
+ ->getMockForAbstractClass();
+ $this->productRepository = $this->getMockBuilder(ProductRepositoryInterface::class)
+ ->setMethods(['getById'])
+ ->getMockForAbstractClass();
+ $this->product = $this->getMockBuilder(Product::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getId', 'getVisibility'])
+ ->getMock();
+ $this->product->expects($this->any())
+ ->method('getId')
+ ->willReturn($this->productId);
+ $this->productRepository->expects($this->any())
+ ->method('getById')
+ ->with($this->productId, false, Store::DEFAULT_STORE_ID)
+ ->willReturn($this->product);
+ $this->productUrlRewriteGenerator = $this->getMockBuilder(ProductUrlRewriteGenerator::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['generate'])
+ ->getMock();
+ $this->event = $this->getMockBuilder(Event::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getProducts'])
+ ->getMock();
+ $this->event->expects($this->any())
+ ->method('getProducts')
+ ->willReturn([$this->productId]);
+ $this->observer = $this->getMockBuilder(Observer::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getEvent'])
+ ->getMock();
+ $this->observer->expects($this->any())
+ ->method('getEvent')
+ ->willReturn($this->event);
+ $this->request = $this->getMockBuilder(RequestInterface::class)
+ ->setMethods(['getParam'])
+ ->getMockForAbstractClass();
+ $this->request->expects($this->any())
+ ->method('getParam')
+ ->with('store_id', Store::DEFAULT_STORE_ID)
+ ->willReturn(Store::DEFAULT_STORE_ID);
+
+ $this->objectManager = new ObjectManager($this);
+ $this->model = $this->objectManager->getObject(
+ ProductToWebsiteChangeObserver::class,
+ [
+ 'productUrlRewriteGenerator' => $this->productUrlRewriteGenerator,
+ 'urlPersist' => $this->urlPersist,
+ 'productRepository' => $this->productRepository,
+ 'request' => $this->request
+ ]
+ );
+ }
+
+ /**
+ * @param array $urlRewriteGeneratorResult
+ * @param int $numberDeleteByData
+ * @param int $productVisibility
+ * @param int $numberReplace
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
+ * @throws \Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException
+ * @dataProvider executeDataProvider
+ */
+ public function testExecute(
+ array $urlRewriteGeneratorResult,
+ int $numberDeleteByData,
+ int $productVisibility,
+ int $numberReplace
+ ) {
+ $this->productUrlRewriteGenerator->expects($this->any())
+ ->method('generate')
+ ->willReturn($urlRewriteGeneratorResult);
+ $this->urlPersist->expects($this->exactly($numberDeleteByData))
+ ->method('deleteByData')
+ ->with(
+ [
+ UrlRewrite::ENTITY_ID => $this->productId,
+ UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE,
+ UrlRewrite::STORE_ID => Store::DEFAULT_STORE_ID
+ ]
+ );
+ $this->product->expects($this->any())
+ ->method('getVisibility')
+ ->willReturn($productVisibility);
+ $this->urlPersist->expects($this->exactly($numberReplace))
+ ->method('replace')
+ ->with($urlRewriteGeneratorResult);
+
+ $this->model->execute($this->observer);
+ }
+
+ /**
+ * Data provider for testExecute
+ *
+ * @return array
+ */
+ public function executeDataProvider(): array
+ {
+ return [
+ [[], 0, Visibility::VISIBILITY_NOT_VISIBLE, 0],
+ [['someRewrite'], 1, Visibility::VISIBILITY_NOT_VISIBLE, 0],
+ [['someRewrite'], 1, Visibility::VISIBILITY_BOTH, 1],
+ ];
+ }
+}
diff --git a/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php b/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php
index cb462ada0fc91..41f5123f3b772 100644
--- a/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php
+++ b/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php
@@ -11,6 +11,10 @@
use Magento\Framework\Pricing\PriceCurrencyInterface;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\Widget\Block\BlockInterface;
+use Magento\Framework\Url\EncoderInterface;
+use Magento\Framework\View\LayoutFactory;
+use Magento\Catalog\Model\Product;
+use Magento\Framework\App\ActionInterface;
/**
* Catalog Products List widget block
@@ -94,6 +98,21 @@ class ProductsList extends \Magento\Catalog\Block\Product\AbstractProduct implem
*/
private $json;
+ /**
+ * @var LayoutFactory
+ */
+ private $layoutFactory;
+
+ /**
+ * @var \Magento\Framework\Url\EncoderInterface
+ */
+ private $urlEncoder;
+
+ /**
+ * @var \Magento\Framework\View\Element\RendererList
+ */
+ private $rendererListBlock;
+
/**
* @param \Magento\Catalog\Block\Product\Context $context
* @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory
@@ -104,6 +123,10 @@ class ProductsList extends \Magento\Catalog\Block\Product\AbstractProduct implem
* @param \Magento\Widget\Helper\Conditions $conditionsHelper
* @param array $data
* @param Json|null $json
+ * @param LayoutFactory|null $layoutFactory
+ * @param \Magento\Framework\Url\EncoderInterface|null $urlEncoder
+ *
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
\Magento\Catalog\Block\Product\Context $context,
@@ -114,7 +137,9 @@ public function __construct(
\Magento\CatalogWidget\Model\Rule $rule,
\Magento\Widget\Helper\Conditions $conditionsHelper,
array $data = [],
- Json $json = null
+ Json $json = null,
+ LayoutFactory $layoutFactory = null,
+ EncoderInterface $urlEncoder = null
) {
$this->productCollectionFactory = $productCollectionFactory;
$this->catalogProductVisibility = $catalogProductVisibility;
@@ -123,6 +148,8 @@ public function __construct(
$this->rule = $rule;
$this->conditionsHelper = $conditionsHelper;
$this->json = $json ?: ObjectManager::getInstance()->get(Json::class);
+ $this->layoutFactory = $layoutFactory ?: ObjectManager::getInstance()->get(LayoutFactory::class);
+ $this->urlEncoder = $urlEncoder ?: ObjectManager::getInstance()->get(EncoderInterface::class);
parent::__construct(
$context,
$data
@@ -151,6 +178,7 @@ protected function _construct()
* Get key pieces for caching block content
*
* @return array
+ * @SuppressWarnings(PHPMD.RequestAwareBlockMethod)
*/
public function getCacheKeyInfo()
{
@@ -210,6 +238,43 @@ public function getProductPriceHtml(
return $price;
}
+ /**
+ * @inheritdoc
+ */
+ protected function getDetailsRendererList()
+ {
+ if (empty($this->rendererListBlock)) {
+ /** @var $layout \Magento\Framework\View\LayoutInterface */
+ $layout = $this->layoutFactory->create(['cacheable' => false]);
+ $layout->getUpdate()->addHandle('catalog_widget_product_list')->load();
+ $layout->generateXml();
+ $layout->generateElements();
+
+ $this->rendererListBlock = $layout->getBlock('category.product.type.widget.details.renderers');
+ }
+
+ return $this->rendererListBlock;
+ }
+
+ /**
+ * Get post parameters.
+ *
+ * @param Product $product
+ * @return array
+ */
+ public function getAddToCartPostParams(Product $product): array
+ {
+ $url = $this->getAddToCartUrl($product);
+
+ return [
+ 'action' => $url,
+ 'data' => [
+ 'product' => $product->getEntityId(),
+ ActionInterface::PARAM_NAME_URL_ENCODED => $this->urlEncoder->encode($url),
+ ]
+ ];
+ }
+
/**
* {@inheritdoc}
*/
@@ -223,6 +288,7 @@ protected function _beforeToHtml()
* Prepare and return product collection
*
* @return \Magento\Catalog\Model\ResourceModel\Product\Collection
+ * @SuppressWarnings(PHPMD.RequestAwareBlockMethod)
*/
public function createCollection()
{
@@ -400,6 +466,24 @@ private function getPriceCurrency()
}
/**
+ * @inheritdoc
+ */
+ public function getAddToCartUrl($product, $additional = [])
+ {
+ $requestingPageUrl = $this->getRequest()->getParam('requesting_page_url');
+
+ if (!empty($requestingPageUrl)) {
+ $additional['useUencPlaceholder'] = true;
+ $url = parent::getAddToCartUrl($product, $additional);
+ return str_replace('%25uenc%25', $this->urlEncoder->encode($requestingPageUrl), $url);
+ }
+
+ return parent::getAddToCartUrl($product, $additional);
+ }
+
+ /**
+ * Get widget block name
+ *
* @return string
*/
private function getWidgetPagerBlockName()
diff --git a/app/code/Magento/CatalogWidget/Setup/UpgradeData.php b/app/code/Magento/CatalogWidget/Setup/UpgradeData.php
new file mode 100644
index 0000000000000..5ebdbe2390d51
--- /dev/null
+++ b/app/code/Magento/CatalogWidget/Setup/UpgradeData.php
@@ -0,0 +1,103 @@
+serializer = $serializer;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
+ {
+ if (version_compare($context->getVersion(), '2.0.1', '<')) {
+ $this->replaceIsWithIsOneOf($setup);
+ }
+ }
+
+ /**
+ * Replace 'is' condition with 'is one of' in database.
+ *
+ * If 'is' product list condition is used with multiple skus it should be replaced by 'is one of' condition.
+ *
+ * @param ModuleDataSetupInterface $setup
+ */
+ private function replaceIsWithIsOneOf(ModuleDataSetupInterface $setup)
+ {
+ $tableName = $setup->getTable('widget_instance');
+ $connection = $setup->getConnection();
+ $select = $connection->select()
+ ->from(
+ $tableName,
+ [
+ 'instance_id',
+ 'widget_parameters',
+ ]
+ )->where('instance_type = ? ', ProductsList::class);
+
+ $result = $setup->getConnection()->fetchAll($select);
+
+ if ($result) {
+ $updatedData = $this->updateWidgetData($result);
+
+ $connection->insertOnDuplicate(
+ $tableName,
+ $updatedData
+ );
+ }
+ }
+
+ /**
+ * Replace 'is' condition with 'is one of' in widget parameters.
+ *
+ * @param array $result
+ * @return array
+ */
+ private function updateWidgetData(array $result): array
+ {
+ return array_map(
+ function ($widgetData) {
+ $widgetParameters = $this->serializer->unserialize($widgetData['widget_parameters']);
+ foreach ($widgetParameters['conditions'] as &$condition) {
+ if (ConditionProduct::class === $condition['type'] &&
+ 'sku' === $condition['attribute'] &&
+ '==' === $condition['operator']) {
+ $condition['operator'] = '()';
+ }
+ }
+ $widgetData['widget_parameters'] = $this->serializer->serialize($widgetParameters);
+
+ return $widgetData;
+ },
+ $result
+ );
+ }
+}
diff --git a/app/code/Magento/CatalogWidget/etc/module.xml b/app/code/Magento/CatalogWidget/etc/module.xml
index 8954f11f954f7..1f2d84bef2d6b 100644
--- a/app/code/Magento/CatalogWidget/etc/module.xml
+++ b/app/code/Magento/CatalogWidget/etc/module.xml
@@ -6,7 +6,7 @@
*/
-->
-
+
diff --git a/app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml b/app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml
new file mode 100644
index 0000000000000..4fe7af7f34683
--- /dev/null
+++ b/app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/CatalogWidget/view/frontend/templates/product/widget/content/grid.phtml b/app/code/Magento/CatalogWidget/view/frontend/templates/product/widget/content/grid.phtml
index 574cbe1107e88..0217838b7fd8b 100644
--- a/app/code/Magento/CatalogWidget/view/frontend/templates/product/widget/content/grid.phtml
+++ b/app/code/Magento/CatalogWidget/view/frontend/templates/product/widget/content/grid.phtml
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+use Magento\Framework\App\Action\Action;
// @codingStandardsIgnoreFile
@@ -48,57 +49,67 @@
= $block->escapeHtml($_item->getName()) ?>
- getProductPriceHtml($_item, $type);
- ?>
-
= $block->getReviewsSummaryHtml($_item, $templateType) ?>
+ = $block->getProductPriceHtml($_item, $type) ?>
+
+ = $block->getProductDetailsHtml($_item) ?>
+
-
-
-
- isSaleable()): ?>
- getTypeInstance()->hasRequiredOptions($_item)): ?>
-
-
- helper('Magento\Framework\Data\Helper\PostHelper');
- $postData = $postDataHelper->getPostData($block->getAddToCartUrl($_item), ['product' => $_item->getEntityId()])
- ?>
-
+
+
+
+
+ isSaleable()): ?>
+ getAddToCartPostParams($_item); ?>
+
+
+
+ getIsSalable()): ?>
+
= $block->escapeHtml(__('In stock')) ?>
+
+
= $block->escapeHtml(__('Out of stock')) ?>
+
+
+
+
+
+
-
-
-
-
+
+
+
diff --git a/app/code/Magento/Checkout/Block/Cart/Sidebar.php b/app/code/Magento/Checkout/Block/Cart/Sidebar.php
index 5c237eecf0a9f..5e3234e9f4cc8 100644
--- a/app/code/Magento/Checkout/Block/Cart/Sidebar.php
+++ b/app/code/Magento/Checkout/Block/Cart/Sidebar.php
@@ -67,7 +67,7 @@ public function __construct(
}
/**
- * Returns minicart config
+ * Returns minicart config.
*
* @return array
*/
@@ -82,7 +82,8 @@ public function getConfig()
'baseUrl' => $this->getBaseUrl(),
'minicartMaxItemsVisible' => $this->getMiniCartMaxItemsCount(),
'websiteId' => $this->_storeManager->getStore()->getWebsiteId(),
- 'maxItemsToDisplay' => $this->getMaxItemsToDisplay()
+ 'maxItemsToDisplay' => $this->getMaxItemsToDisplay(),
+ 'storeId' => $this->_storeManager->getStore()->getId(),
];
}
@@ -132,6 +133,7 @@ public function getShoppingCartUrl()
*
* @return string
* @codeCoverageIgnore
+ * @SuppressWarnings(PHPMD.RequestAwareBlockMethod)
*/
public function getUpdateItemQtyUrl()
{
@@ -143,6 +145,7 @@ public function getUpdateItemQtyUrl()
*
* @return string
* @codeCoverageIgnore
+ * @SuppressWarnings(PHPMD.RequestAwareBlockMethod)
*/
public function getRemoveItemUrl()
{
diff --git a/app/code/Magento/Checkout/Block/Checkout/LayoutProcessor.php b/app/code/Magento/Checkout/Block/Checkout/LayoutProcessor.php
index f47e514948d69..c5d4d68b06225 100644
--- a/app/code/Magento/Checkout/Block/Checkout/LayoutProcessor.php
+++ b/app/code/Magento/Checkout/Block/Checkout/LayoutProcessor.php
@@ -6,8 +6,11 @@
namespace Magento\Checkout\Block\Checkout;
use Magento\Checkout\Helper\Data;
+use Magento\Customer\Model\AttributeMetadataDataProvider;
+use Magento\Customer\Model\Options;
use Magento\Framework\App\ObjectManager;
use Magento\Store\Api\StoreResolverInterface;
+use Magento\Ui\Component\Form\AttributeMapper;
/**
* Class LayoutProcessor
@@ -15,12 +18,12 @@
class LayoutProcessor implements \Magento\Checkout\Block\Checkout\LayoutProcessorInterface
{
/**
- * @var \Magento\Customer\Model\AttributeMetadataDataProvider
+ * @var AttributeMetadataDataProvider
*/
private $attributeMetadataDataProvider;
/**
- * @var \Magento\Ui\Component\Form\AttributeMapper
+ * @var AttributeMapper
*/
protected $attributeMapper;
@@ -30,7 +33,7 @@ class LayoutProcessor implements \Magento\Checkout\Block\Checkout\LayoutProcesso
protected $merger;
/**
- * @var \Magento\Customer\Model\Options
+ * @var Options
*/
private $options;
@@ -50,30 +53,21 @@ class LayoutProcessor implements \Magento\Checkout\Block\Checkout\LayoutProcesso
private $shippingConfig;
/**
- * @param \Magento\Customer\Model\AttributeMetadataDataProvider $attributeMetadataDataProvider
- * @param \Magento\Ui\Component\Form\AttributeMapper $attributeMapper
+ * @param AttributeMetadataDataProvider $attributeMetadataDataProvider
+ * @param AttributeMapper $attributeMapper
* @param AttributeMerger $merger
+ * @param Options|null $options
*/
public function __construct(
- \Magento\Customer\Model\AttributeMetadataDataProvider $attributeMetadataDataProvider,
- \Magento\Ui\Component\Form\AttributeMapper $attributeMapper,
- AttributeMerger $merger
+ AttributeMetadataDataProvider $attributeMetadataDataProvider,
+ AttributeMapper $attributeMapper,
+ AttributeMerger $merger,
+ Options $options = null
) {
$this->attributeMetadataDataProvider = $attributeMetadataDataProvider;
$this->attributeMapper = $attributeMapper;
$this->merger = $merger;
- }
-
- /**
- * @deprecated 100.0.11
- * @return \Magento\Customer\Model\Options
- */
- private function getOptions()
- {
- if (!is_object($this->options)) {
- $this->options = ObjectManager::getInstance()->get(\Magento\Customer\Model\Options::class);
- }
- return $this->options;
+ $this->options = $options ?? ObjectManager::getInstance()->get(Options::class);
}
/**
@@ -143,8 +137,8 @@ private function convertElementsToSelect($elements, $attributesToConvert)
public function process($jsLayout)
{
$attributesToConvert = [
- 'prefix' => [$this->getOptions(), 'getNamePrefixOptions'],
- 'suffix' => [$this->getOptions(), 'getNameSuffixOptions'],
+ 'prefix' => [$this->options, 'getNamePrefixOptions'],
+ 'suffix' => [$this->options, 'getNameSuffixOptions'],
];
$elements = $this->getAddressAttributes();
diff --git a/app/code/Magento/Checkout/Controller/Cart/Addgroup.php b/app/code/Magento/Checkout/Controller/Cart/Addgroup.php
index 8654bdbde5893..1876d3ca37d94 100644
--- a/app/code/Magento/Checkout/Controller/Cart/Addgroup.php
+++ b/app/code/Magento/Checkout/Controller/Cart/Addgroup.php
@@ -41,6 +41,8 @@ public function execute()
}
}
$this->cart->save();
+ } else {
+ $this->messageManager->addErrorMessage(__('Please select at least one product to add to cart'));
}
return $this->_goBack();
}
diff --git a/app/code/Magento/Checkout/CustomerData/Cart.php b/app/code/Magento/Checkout/CustomerData/Cart.php
index 01e91d75c02d9..9154e9c99478e 100644
--- a/app/code/Magento/Checkout/CustomerData/Cart.php
+++ b/app/code/Magento/Checkout/CustomerData/Cart.php
@@ -98,7 +98,8 @@ public function getSectionData()
'items' => $this->getRecentItems(),
'extra_actions' => $this->layout->createBlock(\Magento\Catalog\Block\ShortcutButtons::class)->toHtml(),
'isGuestCheckoutAllowed' => $this->isGuestCheckoutAllowed(),
- 'website_id' => $this->getQuote()->getStore()->getWebsiteId()
+ 'website_id' => $this->getQuote()->getStore()->getWebsiteId(),
+ 'storeId' => $this->getQuote()->getStore()->getStoreId(),
];
}
diff --git a/app/code/Magento/Checkout/Model/Cart.php b/app/code/Magento/Checkout/Model/Cart.php
index c0ba9616754bb..0eb59fc70d92f 100644
--- a/app/code/Magento/Checkout/Model/Cart.php
+++ b/app/code/Magento/Checkout/Model/Cart.php
@@ -15,6 +15,7 @@
* Shopping cart model
*
* @api
+ * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @deprecated 100.1.0 Use \Magento\Quote\Model\Quote instead
*/
@@ -354,22 +355,10 @@ protected function _getProductRequest($requestInfo)
public function addProduct($productInfo, $requestInfo = null)
{
$product = $this->_getProduct($productInfo);
- $request = $this->_getProductRequest($requestInfo);
$productId = $product->getId();
if ($productId) {
- $stockItem = $this->stockRegistry->getStockItem($productId, $product->getStore()->getWebsiteId());
- $minimumQty = $stockItem->getMinSaleQty();
- //If product quantity is not specified in request and there is set minimal qty for it
- if ($minimumQty
- && $minimumQty > 0
- && !$request->getQty()
- ) {
- $request->setQty($minimumQty);
- }
- }
-
- if ($productId) {
+ $request = $this->getQtyRequest($product, $requestInfo);
try {
$result = $this->getQuote()->addProduct($product, $request);
} catch (\Magento\Framework\Exception\LocalizedException $e) {
@@ -425,8 +414,9 @@ public function addProductsByIds($productIds)
}
$product = $this->_getProduct($productId);
if ($product->getId() && $product->isVisibleInCatalog()) {
+ $request = $this->getQtyRequest($product);
try {
- $this->getQuote()->addProduct($product);
+ $this->getQuote()->addProduct($product, $request);
} catch (\Exception $e) {
$allAdded = false;
}
@@ -747,4 +737,26 @@ private function getRequestInfoFilter()
}
return $this->requestInfoFilter;
}
+
+ /**
+ * Get request quantity
+ *
+ * @param Product $product
+ * @param \Magento\Framework\DataObject|int|array $request
+ * @return int|DataObject
+ */
+ private function getQtyRequest($product, $request = 0)
+ {
+ $request = $this->_getProductRequest($request);
+ $stockItem = $this->stockRegistry->getStockItem($product->getId(), $product->getStore()->getWebsiteId());
+ $minimumQty = $stockItem->getMinSaleQty();
+ //If product quantity is not specified in request and there is set minimal qty for it
+ if ($minimumQty
+ && $minimumQty > 0
+ && !$request->getQty()
+ ) {
+ $request->setQty($minimumQty);
+ }
+ return $request;
+ }
}
diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml
index f79d59028c468..d20616b4384d1 100644
--- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml
@@ -7,7 +7,7 @@
-->
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
@@ -37,7 +37,7 @@
-
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml
index 7a5c5e1d15872..3390dc588b69b 100644
--- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingGuestInfoSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingGuestInfoSection.xml
index 8e043f85a0b95..115eb92c48268 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingGuestInfoSection.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingGuestInfoSection.xml
@@ -7,7 +7,7 @@
-->
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml
index 76c9e9f674e6a..b735abc665022 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml
@@ -33,5 +33,9 @@
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCustomerInfoCreatedByGuestTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCustomerInfoCreatedByGuestTest.xml
new file mode 100644
index 0000000000000..32cc254543bca
--- /dev/null
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCustomerInfoCreatedByGuestTest.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml
index c5269ca5d0b56..249c6dafae1a3 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml
@@ -73,7 +73,7 @@
-
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml
new file mode 100644
index 0000000000000..9fee0302345d5
--- /dev/null
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Unit/Block/Cart/SidebarTest.php b/app/code/Magento/Checkout/Test/Unit/Block/Cart/SidebarTest.php
index 88751b899d7c9..015d8ccbe928f 100644
--- a/app/code/Magento/Checkout/Test/Unit/Block/Cart/SidebarTest.php
+++ b/app/code/Magento/Checkout/Test/Unit/Block/Cart/SidebarTest.php
@@ -6,6 +6,8 @@
namespace Magento\Checkout\Test\Unit\Block\Cart;
/**
+ * Unit tests for Magento\Checkout\Block\Cart\Sidebar.
+ *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class SidebarTest extends \PHPUnit\Framework\TestCase
@@ -123,6 +125,11 @@ public function testGetTotalsHtml()
$this->assertEquals($totalsHtml, $this->model->getTotalsHtml());
}
+ /**
+ * Unit test for getConfig method.
+ *
+ * @return void
+ */
public function testGetConfig()
{
$websiteId = 100;
@@ -144,14 +151,15 @@ public function testGetConfig()
'baseUrl' => $baseUrl,
'minicartMaxItemsVisible' => 3,
'websiteId' => 100,
- 'maxItemsToDisplay' => 8
+ 'maxItemsToDisplay' => 8,
+ 'storeId' => null,
];
$valueMap = [
['checkout/cart', [], $shoppingCartUrl],
['checkout', [], $checkoutUrl],
['checkout/sidebar/updateItemQty', ['_secure' => false], $updateItemQtyUrl],
- ['checkout/sidebar/removeItem', ['_secure' => false], $removeItemUrl]
+ ['checkout/sidebar/removeItem', ['_secure' => false], $removeItemUrl],
];
$this->requestMock->expects($this->any())
@@ -161,7 +169,7 @@ public function testGetConfig()
$this->urlBuilderMock->expects($this->exactly(4))
->method('getUrl')
->willReturnMap($valueMap);
- $this->storeManagerMock->expects($this->exactly(2))->method('getStore')->willReturn($storeMock);
+ $this->storeManagerMock->expects($this->atLeastOnce())->method('getStore')->willReturn($storeMock);
$storeMock->expects($this->once())->method('getBaseUrl')->willReturn($baseUrl);
$this->imageHelper->expects($this->once())->method('getFrame')->willReturn(false);
diff --git a/app/code/Magento/Checkout/Test/Unit/CustomerData/CartTest.php b/app/code/Magento/Checkout/Test/Unit/CustomerData/CartTest.php
index 75e181cbabd08..9f718f00b4b9d 100644
--- a/app/code/Magento/Checkout/Test/Unit/CustomerData/CartTest.php
+++ b/app/code/Magento/Checkout/Test/Unit/CustomerData/CartTest.php
@@ -7,6 +7,8 @@
namespace Magento\Checkout\Test\Unit\CustomerData;
/**
+ * Unit tests for Magento\Checkout\CustomerData\Cart.
+ *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class CartTest extends \PHPUnit\Framework\TestCase
@@ -78,6 +80,11 @@ public function testIsGuestCheckoutAllowed()
$this->assertTrue($this->model->isGuestCheckoutAllowed());
}
+ /**
+ * Unit test for getSectionData method.
+ *
+ * @return void
+ */
public function testGetSectionData()
{
$summaryQty = 100;
@@ -113,7 +120,7 @@ public function testGetSectionData()
$storeMock = $this->createPartialMock(\Magento\Store\Model\System\Store::class, ['getWebsiteId']);
$storeMock->expects($this->once())->method('getWebsiteId')->willReturn($websiteId);
- $quoteMock->expects($this->once())->method('getStore')->willReturn($storeMock);
+ $quoteMock->expects($this->atLeastOnce())->method('getStore')->willReturn($storeMock);
$productMock = $this->createPartialMock(
\Magento\Catalog\Model\Product::class,
@@ -162,6 +169,7 @@ public function testGetSectionData()
'isGuestCheckoutAllowed' => 1,
'website_id' => $websiteId,
'subtotalAmount' => 200,
+ 'storeId' => null,
];
$this->assertEquals($expectedResult, $this->model->getSectionData());
}
@@ -199,7 +207,7 @@ public function testGetSectionDataWithCompositeProduct()
$storeMock = $this->createPartialMock(\Magento\Store\Model\System\Store::class, ['getWebsiteId']);
$storeMock->expects($this->once())->method('getWebsiteId')->willReturn($websiteId);
- $quoteMock->expects($this->once())->method('getStore')->willReturn($storeMock);
+ $quoteMock->expects($this->atLeastOnce())->method('getStore')->willReturn($storeMock);
$this->checkoutCartMock->expects($this->once())->method('getSummaryQty')->willReturn($summaryQty);
$this->checkoutHelperMock->expects($this->once())
@@ -265,6 +273,7 @@ public function testGetSectionDataWithCompositeProduct()
'isGuestCheckoutAllowed' => 1,
'website_id' => $websiteId,
'subtotalAmount' => 200,
+ 'storeId' => null,
];
$this->assertEquals($expectedResult, $this->model->getSectionData());
}
diff --git a/app/code/Magento/Checkout/etc/di.xml b/app/code/Magento/Checkout/etc/di.xml
index 71dfd12bb4779..4ebd594a28562 100644
--- a/app/code/Magento/Checkout/etc/di.xml
+++ b/app/code/Magento/Checkout/etc/di.xml
@@ -49,7 +49,4 @@
-
-
-
diff --git a/app/code/Magento/Checkout/etc/frontend/di.xml b/app/code/Magento/Checkout/etc/frontend/di.xml
index 00bcd2a27005a..8f35fe9f37abf 100644
--- a/app/code/Magento/Checkout/etc/frontend/di.xml
+++ b/app/code/Magento/Checkout/etc/frontend/di.xml
@@ -96,4 +96,7 @@
+
+
+
diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml
index 1005c11e44d95..84ab9b13d8f3a 100644
--- a/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml
+++ b/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml
@@ -14,7 +14,8 @@
method="post"
id="form-validate"
data-mage-init='{"Magento_Checkout/js/action/update-shopping-cart":
- {"validationURL" : "/checkout/cart/updateItemQty"}
+ {"validationURL" : "/checkout/cart/updateItemQty",
+ "updateCartActionContainer": "#update_cart_action_container"}
}'
class="form form-cart">
= $block->getBlockHtml('formkey') ?>
diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/noItems.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/noItems.phtml
index 1c0c221a550cd..67ac4a9335565 100644
--- a/app/code/Magento/Checkout/view/frontend/templates/cart/noItems.phtml
+++ b/app/code/Magento/Checkout/view/frontend/templates/cart/noItems.phtml
@@ -13,3 +13,10 @@
$block->escapeUrl($block->getContinueShoppingUrl())) ?>
= $block->getChildHtml('shopping.cart.table.after') ?>
+
\ No newline at end of file
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js b/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js
index ce1527b3d72d6..1920bc4d7ac41 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js
@@ -14,7 +14,8 @@ define([
$.widget('mage.updateShoppingCart', {
options: {
validationURL: '',
- eventName: 'updateCartItemQty'
+ eventName: 'updateCartItemQty',
+ updateCartActionContainer: ''
},
/** @inheritdoc */
@@ -31,7 +32,9 @@ define([
* @return {Boolean}
*/
onSubmit: function (event) {
- if (!this.options.validationURL) {
+ var action = this.element.find(this.options.updateCartActionContainer).val();
+
+ if (!this.options.validationURL || action === 'empty_cart') {
return true;
}
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js b/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js
new file mode 100644
index 0000000000000..27d38697afe39
--- /dev/null
+++ b/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js
@@ -0,0 +1,12 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+define([
+ 'Magento_Customer/js/customer-data'
+], function (customerData) {
+ 'use strict';
+
+ customerData.reload(['cart'], false);
+});
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js b/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js
index 6f9a1a46826da..d68b0682eb511 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js
@@ -202,6 +202,13 @@ function (
}
},
+ /**
+ * Manage cancel button visibility
+ */
+ canUseCancelBillingAddress: ko.computed(function () {
+ return quote.billingAddress() || lastSelectedBillingAddress;
+ }),
+
/**
* Restore billing address
*/
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js b/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js
index a2f8c8c56ff33..5e29fa209a641 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js
@@ -81,6 +81,7 @@ define([
maxItemsToDisplay: window.checkout.maxItemsToDisplay,
cart: {},
+ // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
/**
* @override
*/
@@ -101,12 +102,16 @@ define([
self.isLoading(true);
});
- if (cartData()['website_id'] !== window.checkout.websiteId) {
+ if (cartData().website_id !== window.checkout.websiteId ||
+ cartData().store_id !== window.checkout.storeId
+ ) {
customerData.reload(['cart'], false);
}
return this._super();
},
+ //jscs:enable requireCamelCaseOrUpperCaseIdentifiers
+
isLoading: ko.observable(false),
initSidebar: initSidebar,
diff --git a/app/code/Magento/Checkout/view/frontend/web/template/billing-address.html b/app/code/Magento/Checkout/view/frontend/web/template/billing-address.html
index 5f735fbb4daa9..63edb5057b933 100644
--- a/app/code/Magento/Checkout/view/frontend/web/template/billing-address.html
+++ b/app/code/Magento/Checkout/view/frontend/web/template/billing-address.html
@@ -22,7 +22,7 @@