diff --git a/app/code/Magento/Catalog/Api/CategoryLinkRepositoryInterface.php b/app/code/Magento/Catalog/Api/CategoryLinkRepositoryInterface.php
index 30ac06107ba1d..34656d522a72c 100644
--- a/app/code/Magento/Catalog/Api/CategoryLinkRepositoryInterface.php
+++ b/app/code/Magento/Catalog/Api/CategoryLinkRepositoryInterface.php
@@ -32,18 +32,20 @@ public function save(\Magento\Catalog\Api\Data\CategoryProductLinkInterface $pro
*
* @throws \Magento\Framework\Exception\CouldNotSaveException
* @throws \Magento\Framework\Exception\StateException
+ * @throws \Magento\Framework\Exception\InputException
*/
public function delete(\Magento\Catalog\Api\Data\CategoryProductLinkInterface $productLink);
/**
* Remove the product assignment from the category by category id and sku
*
- * @param string $sku
+ * @param string $categoryId
* @param string $sku
* @return bool will returned True if products successfully deleted
*
* @throws \Magento\Framework\Exception\CouldNotSaveException
* @throws \Magento\Framework\Exception\StateException
+ * @throws \Magento\Framework\Exception\InputException
*/
public function deleteByIds($categoryId, $sku);
}
diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Category/Helper/Pricestep.php b/app/code/Magento/Catalog/Block/Adminhtml/Category/Helper/Pricestep.php
index b77a5e2e95241..3266922d116ec 100644
--- a/app/code/Magento/Catalog/Block/Adminhtml/Category/Helper/Pricestep.php
+++ b/app/code/Magento/Catalog/Block/Adminhtml/Category/Helper/Pricestep.php
@@ -11,6 +11,9 @@
*/
namespace Magento\Catalog\Block\Adminhtml\Category\Helper;
+/**
+ * Pricestep Helper
+ */
class Pricestep extends \Magento\Framework\Data\Form\Element\Text
{
/**
@@ -40,7 +43,7 @@ public function getElementHtml()
$disabled = true;
}
- parent::addClass('validate-number validate-number-range number-range-0.01-1000000000');
+ parent::addClass('validate-number validate-number-range number-range-0.01-9999999999999999');
$html = parent::getElementHtml();
$htmlId = 'use_config_' . $this->getHtmlId();
$html .= '
*/
use Magento\Catalog\Model\Entity\Product\Attribute\Group\AttributeMapperInterface;
/**
+ * Adminhtml Catalog Attribute Set Main Block.
+ *
* @api
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @since 100.0.2
@@ -140,7 +140,7 @@ protected function _prepareLayout()
) . '\', \'' . $this->getUrl(
'catalog/*/delete',
['id' => $setId]
- ) . '\')',
+ ) . '\',{data: {}})',
'class' => 'delete'
]
);
diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Crosssell.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Crosssell.php
index e4f700e5790a2..e5ce59c550af1 100644
--- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Crosssell.php
+++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Crosssell.php
@@ -10,9 +10,13 @@
use Magento\Catalog\Model\Product;
/**
+ * Crossel product edit tab
+ *
* @api
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @since 100.0.2
+ * @deprecated Not used since cross-sell products grid moved to UI components.
+ * @see \Magento\Catalog\Ui\DataProvider\Product\Related\CrossSellDataProvider
*/
class Crosssell extends Extended
{
diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Related.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Related.php
index 0c59a7402dac1..23b927598e8e7 100644
--- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Related.php
+++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Related.php
@@ -9,8 +9,12 @@
use Magento\Backend\Block\Widget\Grid\Extended;
/**
+ * Related product edit tab
+ *
* @api
* @since 100.0.2
+ * @deprecated Not used since related products grid moved to UI components.
+ * @see \Magento\Catalog\Ui\DataProvider\Product\Related\RelatedDataProvider
*/
class Related extends Extended
{
@@ -318,7 +322,7 @@ protected function _prepareColumns()
}
/**
- * Rerieve grid URL
+ * Retrieve grid URL
*
* @return string
*/
diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Upsell.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Upsell.php
index 323b1785bc96e..41ad72ca39e53 100644
--- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Upsell.php
+++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Upsell.php
@@ -6,8 +6,12 @@
namespace Magento\Catalog\Block\Adminhtml\Product\Edit\Tab;
/**
+ * Upsell product edit tab
+ *
* @api
* @since 100.0.2
+ * @deprecated Not used since upsell products grid moved to UI components.
+ * @see \Magento\Catalog\Ui\DataProvider\Product\Related\CrossSellDataProvider
*/
class Upsell extends \Magento\Backend\Block\Widget\Grid\Extended
{
diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Index.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Index.php
index 5089b37f90c58..a5be6223bee75 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Index.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Index.php
@@ -8,6 +8,9 @@
use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface;
+/**
+ * Controller for category listing
+ */
class Index extends \Magento\Catalog\Controller\Adminhtml\Category implements HttpGetActionInterface
{
/**
diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php
index 49e601357c605..39ed11b1806cd 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php
@@ -197,12 +197,12 @@ public function execute()
$attributeCode = $attributeCode ?: $this->generateCode($this->getRequest()->getParam('frontend_label')[0]);
if (strlen($attributeCode) > 0) {
$validatorAttrCode = new \Zend_Validate_Regex(
- ['pattern' => '/^[a-z\x{600}-\x{6FF}][a-z\x{600}-\x{6FF}_0-9]{0,30}$/u']
+ ['pattern' => '/^[a-zA-Z\x{600}-\x{6FF}][a-zA-Z\x{600}-\x{6FF}_0-9]{0,30}$/u']
);
if (!$validatorAttrCode->isValid($attributeCode)) {
$this->messageManager->addErrorMessage(
__(
- 'Attribute code "%1" is invalid. Please use only letters (a-z), ' .
+ 'Attribute code "%1" is invalid. Please use only letters (a-z or A-Z), ' .
'numbers (0-9) or underscore(_) in this field, first character should be a letter.',
$attributeCode
)
diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Crosssell.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Crosssell.php
index fa94a2fb7be2c..e51d3ffe94ae2 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Crosssell.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Crosssell.php
@@ -6,7 +6,17 @@
*/
namespace Magento\Catalog\Controller\Adminhtml\Product;
-class Crosssell extends \Magento\Catalog\Controller\Adminhtml\Product
+use Magento\Catalog\Controller\Adminhtml\Product;
+use Magento\Framework\App\Action\HttpPostActionInterface;
+
+/**
+ * Class Crosssell
+ *
+ * @package Magento\Catalog\Controller\Adminhtml\Product
+ * @deprecated Not used since cross-sell products grid moved to UI components.
+ * @see Magento_Catalog::view/adminhtml/ui_component/crosssell_product_listing.xml
+ */
+class Crosssell extends Product implements HttpPostActionInterface
{
/**
* @var \Magento\Framework\View\Result\LayoutFactory
diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/CrosssellGrid.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/CrosssellGrid.php
index daaf2c21ee2b9..5039d0c052b5d 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/CrosssellGrid.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/CrosssellGrid.php
@@ -6,7 +6,17 @@
*/
namespace Magento\Catalog\Controller\Adminhtml\Product;
-class CrosssellGrid extends \Magento\Catalog\Controller\Adminhtml\Product
+use Magento\Catalog\Controller\Adminhtml\Product;
+use Magento\Framework\App\Action\HttpPostActionInterface;
+
+/**
+ * Class CrosssellGrid
+ *
+ * @package Magento\Catalog\Controller\Adminhtml\Product
+ * @deprecated Not used since cross-sell products grid moved to UI components.
+ * @see Magento_Catalog::view/adminhtml/ui_component/crosssell_product_listing.xml
+ */
+class CrosssellGrid extends Product implements HttpPostActionInterface
{
/**
* @var \Magento\Framework\View\Result\LayoutFactory
diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Related.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Related.php
index 4bc15d14a29b1..f54f8d469c3e8 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Related.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Related.php
@@ -7,7 +7,17 @@
*/
namespace Magento\Catalog\Controller\Adminhtml\Product;
-class Related extends \Magento\Catalog\Controller\Adminhtml\Product
+use Magento\Catalog\Controller\Adminhtml\Product;
+use Magento\Framework\App\Action\HttpPostActionInterface;
+
+/**
+ * Class Related
+ *
+ * @package Magento\Catalog\Controller\Adminhtml\Product
+ * @deprecated Not used since related products grid moved to UI components.
+ * @see Magento_Catalog::view/adminhtml/ui_component/related_product_listing.xml
+ */
+class Related extends Product implements HttpPostActionInterface
{
/**
* @var \Magento\Framework\View\Result\LayoutFactory
@@ -29,6 +39,8 @@ public function __construct(
}
/**
+ * Execute
+ *
* @return \Magento\Framework\View\Result\Layout
*/
public function execute()
diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/RelatedGrid.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/RelatedGrid.php
index b2fc7ebe1eb31..b1092bba0d369 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/RelatedGrid.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/RelatedGrid.php
@@ -7,6 +7,15 @@
*/
namespace Magento\Catalog\Controller\Adminhtml\Product;
-class RelatedGrid extends Related
+use Magento\Framework\App\Action\HttpPostActionInterface;
+
+/**
+ * Class RelatedGrid
+ *
+ * @package Magento\Catalog\Controller\Adminhtml\Product
+ * @deprecated Not used since related products grid moved to UI components.
+ * @see Magento_Catalog::view/adminhtml/ui_component/related_product_listing.xml
+ */
+class RelatedGrid extends Related implements HttpPostActionInterface
{
}
diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Upsell.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Upsell.php
index 614bddd0ebc87..1cec8e8678797 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Upsell.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Upsell.php
@@ -6,7 +6,17 @@
*/
namespace Magento\Catalog\Controller\Adminhtml\Product;
-class Upsell extends \Magento\Catalog\Controller\Adminhtml\Product
+use Magento\Catalog\Controller\Adminhtml\Product;
+use Magento\Framework\App\Action\HttpPostActionInterface;
+
+/**
+ * Class Upsell
+ *
+ * @package Magento\Catalog\Controller\Adminhtml\Product
+ * @deprecated Not used since upsell products grid moved to UI components.
+ * @see Magento_Catalog::view/adminhtml/ui_component/upsell_product_listing.xml
+ */
+class Upsell extends Product implements HttpPostActionInterface
{
/**
* @var \Magento\Framework\View\Result\LayoutFactory
diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/UpsellGrid.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/UpsellGrid.php
index 50beb588b15df..581531e7c93fb 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/UpsellGrid.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/UpsellGrid.php
@@ -6,7 +6,17 @@
*/
namespace Magento\Catalog\Controller\Adminhtml\Product;
-class UpsellGrid extends \Magento\Catalog\Controller\Adminhtml\Product
+use Magento\Catalog\Controller\Adminhtml\Product;
+use Magento\Framework\App\Action\HttpPostActionInterface;
+
+/**
+ * Class UpsellGrid
+ *
+ * @package Magento\Catalog\Controller\Adminhtml\Product
+ * @deprecated Not used since upsell products grid moved to UI components.
+ * @see Magento_Catalog::view/adminhtml/ui_component/upsell_product_listing.xml
+ */
+class UpsellGrid extends Product implements HttpPostActionInterface
{
/**
* @var \Magento\Framework\View\Result\LayoutFactory
diff --git a/app/code/Magento/Catalog/Cron/DeleteOutdatedPriceValues.php b/app/code/Magento/Catalog/Cron/DeleteOutdatedPriceValues.php
index 7cc3eb9e3d2da..25f6d0c323687 100644
--- a/app/code/Magento/Catalog/Cron/DeleteOutdatedPriceValues.php
+++ b/app/code/Magento/Catalog/Cron/DeleteOutdatedPriceValues.php
@@ -12,6 +12,8 @@
use Magento\Store\Model\Store;
/**
+ * Cron job for removing outdated prices.
+ *
* Cron operation is responsible for deleting all product prices on WEBSITE level
* in case 'Catalog Price Scope' configuration parameter is set to GLOBAL.
*/
@@ -76,7 +78,7 @@ public function execute()
/**
* Checks if price scope config option explicitly equal to global value.
*
- * Such strict comparision is required to prevent price deleting when
+ * Such strict comparison is required to prevent price deleting when
* price scope config option is null for some reason.
*
* @return bool
diff --git a/app/code/Magento/Catalog/Model/Category.php b/app/code/Magento/Catalog/Model/Category.php
index 29fb0d282a87c..1f62bed93e23f 100644
--- a/app/code/Magento/Catalog/Model/Category.php
+++ b/app/code/Magento/Catalog/Model/Category.php
@@ -1152,13 +1152,14 @@ public function getIdentities()
$identities = [
self::CACHE_TAG . '_' . $this->getId(),
];
- if (!$this->getId() || $this->hasDataChanges()
- || $this->isDeleted() || $this->dataHasChangedFor(self::KEY_INCLUDE_IN_MENU)
- ) {
+ if ($this->hasDataChanges()) {
+ $identities[] = Product::CACHE_PRODUCT_CATEGORY_TAG . '_' . $this->getId();
+ }
+ if (!$this->getId() || $this->isDeleted() || $this->dataHasChangedFor(self::KEY_INCLUDE_IN_MENU)) {
$identities[] = self::CACHE_TAG;
$identities[] = Product::CACHE_PRODUCT_CATEGORY_TAG . '_' . $this->getId();
}
- return $identities;
+ return array_unique($identities);
}
/**
diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php
index fbe0d4b550fa6..2252b3e3d5506 100644
--- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php
+++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php
@@ -10,7 +10,8 @@
use Magento\Framework\EntityManager\MetadataPool;
/**
- * Class FlatTableBuilder
+ * Class for building flat index
+ *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class FlatTableBuilder
@@ -346,12 +347,21 @@ protected function _updateTemporaryTableByStoreValues(
}
//Update not simple attributes (eg. dropdown)
- if (isset($flatColumns[$attributeCode . $valueFieldSuffix])) {
- $select = $this->_connection->select()->joinInner(
- ['t' => $this->_productIndexerHelper->getTable('eav_attribute_option_value')],
- 't.option_id = et.' . $attributeCode . ' AND t.store_id=' . $storeId,
- [$attributeCode . $valueFieldSuffix => 't.value']
- );
+ $columnName = $attributeCode . $valueFieldSuffix;
+ if (isset($flatColumns[$columnName])) {
+ $columnValue = $this->_connection->getIfNullSql('ts.value', 't0.value');
+ $select = $this->_connection->select();
+ $select->joinLeft(
+ ['t0' => $this->_productIndexerHelper->getTable('eav_attribute_option_value')],
+ 't0.option_id = et.' . $attributeCode . ' AND t0.store_id = 0',
+ []
+ )->joinLeft(
+ ['ts' => $this->_productIndexerHelper->getTable('eav_attribute_option_value')],
+ 'ts.option_id = et.' . $attributeCode . ' AND ts.store_id = ' . $storeId,
+ []
+ )->columns(
+ [$columnName => $columnValue]
+ )->where($columnValue . ' IS NOT NULL');
if (!empty($changedIds)) {
$select->where($this->_connection->quoteInto('et.entity_id IN (?)', $changedIds));
}
@@ -374,6 +384,8 @@ protected function _getTemporaryTableName($tableName)
}
/**
+ * Get metadata pool
+ *
* @return \Magento\Framework\EntityManager\MetadataPool
*/
private function getMetadataPool()
diff --git a/app/code/Magento/Catalog/Model/Layer/Filter/AbstractFilter.php b/app/code/Magento/Catalog/Model/Layer/Filter/AbstractFilter.php
index d21a8666ec0ac..f2e2e67f944e9 100644
--- a/app/code/Magento/Catalog/Model/Layer/Filter/AbstractFilter.php
+++ b/app/code/Magento/Catalog/Model/Layer/Filter/AbstractFilter.php
@@ -139,7 +139,7 @@ public function apply(\Magento\Framework\App\RequestInterface $request)
}
/**
- * Get fiter items count
+ * Get filter items count
*
* @return int
*/
diff --git a/app/code/Magento/Catalog/Model/Layer/Filter/Item/DataBuilder.php b/app/code/Magento/Catalog/Model/Layer/Filter/Item/DataBuilder.php
index 4d2878b0b1e84..07c9c2eaa2491 100644
--- a/app/code/Magento/Catalog/Model/Layer/Filter/Item/DataBuilder.php
+++ b/app/code/Magento/Catalog/Model/Layer/Filter/Item/DataBuilder.php
@@ -4,11 +4,11 @@
* See COPYING.txt for license details.
*/
+namespace Magento\Catalog\Model\Layer\Filter\Item;
+
/**
* Item Data Builder
*/
-namespace Magento\Catalog\Model\Layer\Filter\Item;
-
class DataBuilder
{
/**
@@ -29,7 +29,7 @@ class DataBuilder
* Add Item Data
*
* @param string $label
- * @param string $label
+ * @param string $value
* @param int $count
* @return void
*/
diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php
index 44c5c891f4d6a..5d47e85768fad 100644
--- a/app/code/Magento/Catalog/Model/Product.php
+++ b/app/code/Magento/Catalog/Model/Product.php
@@ -727,7 +727,7 @@ public function getIdBySku($sku)
public function getCategoryId()
{
$category = $this->_registry->registry('current_category');
- if ($category) {
+ if ($category && in_array($category->getId(), $this->getCategoryIds())) {
return $category->getId();
}
return false;
diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Boolean.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Boolean.php
index c2108b0273bdb..be1e523960f27 100644
--- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Boolean.php
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Boolean.php
@@ -3,6 +3,8 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Catalog\Model\Product\Attribute\Backend;
use Magento\Catalog\Model\Product\Attribute\Source\Boolean as BooleanSource;
@@ -25,7 +27,9 @@ public function beforeSave($object)
$attributeCode = $this->getAttribute()->getName();
if ($object->getData('use_config_' . $attributeCode)) {
$object->setData($attributeCode, BooleanSource::VALUE_USE_CONFIG);
+ return $this;
}
- return $this;
+
+ return parent::beforeSave($object);
}
}
diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/AbstractHandler.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/AbstractHandler.php
new file mode 100644
index 0000000000000..fc0f090937db9
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/AbstractHandler.php
@@ -0,0 +1,101 @@
+groupManagement = $groupManagement;
+ }
+
+ /**
+ * Get additional tier price fields.
+ *
+ * @param array $objectArray
+ * @return array
+ */
+ protected function getAdditionalFields(array $objectArray): array
+ {
+ $percentageValue = $this->getPercentage($objectArray);
+
+ return [
+ 'value' => $percentageValue ? null : $objectArray['price'],
+ 'percentage_value' => $percentageValue ?: null,
+ ];
+ }
+
+ /**
+ * Check whether price has percentage value.
+ *
+ * @param array $priceRow
+ * @return float|null
+ */
+ protected function getPercentage(array $priceRow): ?float
+ {
+ return isset($priceRow['percentage_value']) && is_numeric($priceRow['percentage_value'])
+ ? (float)$priceRow['percentage_value']
+ : null;
+ }
+
+ /**
+ * Prepare tier price data by provided price row data.
+ *
+ * @param array $data
+ * @return array
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ protected function prepareTierPrice(array $data): array
+ {
+ $useForAllGroups = (int)$data['cust_group'] === $this->groupManagement->getAllCustomersGroup()->getId();
+ $customerGroupId = $useForAllGroups ? 0 : $data['cust_group'];
+ $tierPrice = array_merge(
+ $this->getAdditionalFields($data),
+ [
+ 'website_id' => $data['website_id'],
+ 'all_groups' => (int)$useForAllGroups,
+ 'customer_group_id' => $customerGroupId,
+ 'value' => $data['price'] ?? null,
+ 'qty' => $this->parseQty($data['price_qty']),
+ ]
+ );
+
+ return $tierPrice;
+ }
+
+ /**
+ * Parse quantity value into float.
+ *
+ * @param mixed $value
+ * @return float|int
+ */
+ protected function parseQty($value)
+ {
+ return $value * 1;
+ }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/SaveHandler.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/SaveHandler.php
index 248d8ed221250..9cb2ac0145898 100644
--- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/SaveHandler.php
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/SaveHandler.php
@@ -18,7 +18,7 @@
/**
* Process tier price data for handled new product
*/
-class SaveHandler implements ExtensionInterface
+class SaveHandler extends AbstractHandler
{
/**
* @var \Magento\Store\Model\StoreManagerInterface
@@ -30,11 +30,6 @@ class SaveHandler implements ExtensionInterface
*/
private $attributeRepository;
- /**
- * @var \Magento\Customer\Api\GroupManagementInterface
- */
- private $groupManagement;
-
/**
* @var \Magento\Framework\EntityManager\MetadataPool
*/
@@ -59,9 +54,10 @@ public function __construct(
MetadataPool $metadataPool,
Tierprice $tierPriceResource
) {
+ parent::__construct($groupManagement);
+
$this->storeManager = $storeManager;
$this->attributeRepository = $attributeRepository;
- $this->groupManagement = $groupManagement;
$this->metadataPoll = $metadataPool;
$this->tierPriceResource = $tierPriceResource;
}
@@ -72,8 +68,6 @@ public function __construct(
* @param \Magento\Catalog\Api\Data\ProductInterface|object $entity
* @param array $arguments
* @return \Magento\Catalog\Api\Data\ProductInterface|object
- * @throws \Magento\Framework\Exception\NoSuchEntityException
- * @throws \Magento\Framework\Exception\LocalizedException
* @throws \Magento\Framework\Exception\InputException
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
@@ -115,57 +109,4 @@ public function execute($entity, $arguments = [])
return $entity;
}
-
- /**
- * Get additional tier price fields
- *
- * @param array $objectArray
- * @return array
- */
- private function getAdditionalFields(array $objectArray): array
- {
- $percentageValue = $this->getPercentage($objectArray);
- return [
- 'value' => $percentageValue ? null : $objectArray['price'],
- 'percentage_value' => $percentageValue ?: null,
- ];
- }
-
- /**
- * Check whether price has percentage value.
- *
- * @param array $priceRow
- * @return int|null
- */
- private function getPercentage(array $priceRow): ?int
- {
- return isset($priceRow['percentage_value']) && is_numeric($priceRow['percentage_value'])
- ? (int)$priceRow['percentage_value']
- : null;
- }
-
- /**
- * Prepare tier price data by provided price row data
- *
- * @param array $data
- * @return array
- * @throws \Magento\Framework\Exception\LocalizedException
- */
- private function prepareTierPrice(array $data): array
- {
- $useForAllGroups = (int)$data['cust_group'] === $this->groupManagement->getAllCustomersGroup()->getId();
- $customerGroupId = $useForAllGroups ? 0 : $data['cust_group'];
- $tierPrice = array_merge(
- $this->getAdditionalFields($data),
- [
- 'website_id' => $data['website_id'],
- 'all_groups' => (int)$useForAllGroups,
- 'customer_group_id' => $customerGroupId,
- 'value' => $data['price'] ?? null,
- 'qty' => (int)$data['price_qty']
- ]
- );
-
- return $tierPrice;
- }
}
diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php
index aef3e87586015..663b7facf4257 100644
--- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php
@@ -16,9 +16,9 @@
use Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice;
/**
- * Process tier price data for handled existing product
+ * Process tier price data for handled existing product.
*/
-class UpdateHandler implements ExtensionInterface
+class UpdateHandler extends AbstractHandler
{
/**
* @var \Magento\Store\Model\StoreManagerInterface
@@ -30,11 +30,6 @@ class UpdateHandler implements ExtensionInterface
*/
private $attributeRepository;
- /**
- * @var \Magento\Customer\Api\GroupManagementInterface
- */
- private $groupManagement;
-
/**
* @var \Magento\Framework\EntityManager\MetadataPool
*/
@@ -59,9 +54,10 @@ public function __construct(
MetadataPool $metadataPool,
Tierprice $tierPriceResource
) {
+ parent::__construct($groupManagement);
+
$this->storeManager = $storeManager;
$this->attributeRepository = $attributeRepository;
- $this->groupManagement = $groupManagement;
$this->metadataPoll = $metadataPool;
$this->tierPriceResource = $tierPriceResource;
}
@@ -72,8 +68,7 @@ public function __construct(
* @param \Magento\Catalog\Api\Data\ProductInterface|object $entity
* @param array $arguments
* @return \Magento\Catalog\Api\Data\ProductInterface|object
- * @throws \Magento\Framework\Exception\NoSuchEntityException
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws \Magento\Framework\Exception\InputException
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function execute($entity, $arguments = [])
@@ -119,34 +114,6 @@ public function execute($entity, $arguments = [])
return $entity;
}
- /**
- * Get additional tier price fields
- *
- * @param array $objectArray
- * @return array
- */
- private function getAdditionalFields(array $objectArray): array
- {
- $percentageValue = $this->getPercentage($objectArray);
- return [
- 'value' => $percentageValue ? null : $objectArray['price'],
- 'percentage_value' => $percentageValue ?: null,
- ];
- }
-
- /**
- * Check whether price has percentage value.
- *
- * @param array $priceRow
- * @return int|null
- */
- private function getPercentage(array $priceRow): ?int
- {
- return isset($priceRow['percentage_value']) && is_numeric($priceRow['percentage_value'])
- ? (int)$priceRow['percentage_value']
- : null;
- }
-
/**
* Update existing tier prices for processed product
*
@@ -226,39 +193,15 @@ private function deleteValues(int $productId, array $valuesToDelete): bool
*/
private function getPriceKey(array $priceData): string
{
+ $qty = $this->parseQty($priceData['price_qty']);
$key = implode(
'-',
- array_merge([$priceData['website_id'], $priceData['cust_group']], [(int)$priceData['price_qty']])
+ array_merge([$priceData['website_id'], $priceData['cust_group']], [$qty])
);
return $key;
}
- /**
- * Prepare tier price data by provided price row data
- *
- * @param array $data
- * @return array
- * @throws \Magento\Framework\Exception\LocalizedException
- */
- private function prepareTierPrice(array $data): array
- {
- $useForAllGroups = (int)$data['cust_group'] === $this->groupManagement->getAllCustomersGroup()->getId();
- $customerGroupId = $useForAllGroups ? 0 : $data['cust_group'];
- $tierPrice = array_merge(
- $this->getAdditionalFields($data),
- [
- 'website_id' => $data['website_id'],
- 'all_groups' => (int)$useForAllGroups,
- 'customer_group_id' => $customerGroupId,
- 'value' => $data['price'] ?? null,
- 'qty' => (int)$data['price_qty']
- ]
- );
-
- return $tierPrice;
- }
-
/**
* Check by id is website global
*
diff --git a/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php b/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php
index 99d5016f5cdb9..08455430ccac8 100644
--- a/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php
+++ b/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php
@@ -28,13 +28,20 @@ class DefaultValidator extends \Magento\Framework\Validator\AbstractValidator
*/
protected $priceTypes;
+ /**
+ * @var \Magento\Framework\Locale\FormatInterface
+ */
+ private $localeFormat;
+
/**
* @param \Magento\Catalog\Model\ProductOptions\ConfigInterface $productOptionConfig
* @param \Magento\Catalog\Model\Config\Source\Product\Options\Price $priceConfig
+ * @param \Magento\Framework\Locale\FormatInterface|null $localeFormat
*/
public function __construct(
\Magento\Catalog\Model\ProductOptions\ConfigInterface $productOptionConfig,
- \Magento\Catalog\Model\Config\Source\Product\Options\Price $priceConfig
+ \Magento\Catalog\Model\Config\Source\Product\Options\Price $priceConfig,
+ \Magento\Framework\Locale\FormatInterface $localeFormat = null
) {
foreach ($productOptionConfig->getAll() as $option) {
foreach ($option['types'] as $type) {
@@ -45,6 +52,9 @@ public function __construct(
foreach ($priceConfig->toOptionArray() as $item) {
$this->priceTypes[] = $item['value'];
}
+
+ $this->localeFormat = $localeFormat ?: \Magento\Framework\App\ObjectManager::getInstance()
+ ->get(\Magento\Framework\Locale\FormatInterface::class);
}
/**
@@ -137,11 +147,11 @@ protected function validateOptionType(Option $option)
*/
protected function validateOptionValue(Option $option)
{
- return $this->isInRange($option->getPriceType(), $this->priceTypes);
+ return $this->isInRange($option->getPriceType(), $this->priceTypes) && $this->isNumber($option->getPrice());
}
/**
- * Check whether value is empty
+ * Check whether the value is empty
*
* @param mixed $value
* @return bool
@@ -152,7 +162,7 @@ protected function isEmpty($value)
}
/**
- * Check whether value is in range
+ * Check whether the value is in range
*
* @param string $value
* @param array $range
@@ -164,13 +174,24 @@ protected function isInRange($value, array $range)
}
/**
- * Check whether value is not negative
+ * Check whether the value is negative
*
* @param string $value
* @return bool
*/
protected function isNegative($value)
{
- return (int) $value < 0;
+ return $this->localeFormat->getNumber($value) < 0;
+ }
+
+ /**
+ * Check whether the value is a number
+ *
+ * @param string $value
+ * @return bool
+ */
+ public function isNumber($value)
+ {
+ return is_numeric($this->localeFormat->getNumber($value));
}
}
diff --git a/app/code/Magento/Catalog/Model/Product/Option/Validator/Select.php b/app/code/Magento/Catalog/Model/Product/Option/Validator/Select.php
index 44756890b6ed7..209531f599811 100644
--- a/app/code/Magento/Catalog/Model/Product/Option/Validator/Select.php
+++ b/app/code/Magento/Catalog/Model/Product/Option/Validator/Select.php
@@ -8,6 +8,9 @@
use Magento\Catalog\Model\Product\Option;
+/**
+ * Select validator class
+ */
class Select extends DefaultValidator
{
/**
@@ -83,7 +86,7 @@ protected function isValidOptionPrice($priceType, $price, $storeId)
if (!$priceType && !$price) {
return true;
}
- if (!$this->isInRange($priceType, $this->priceTypes)) {
+ if (!$this->isInRange($priceType, $this->priceTypes) || !$this->isNumber($price)) {
return false;
}
diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php
index 1bddd2d07cd81..3ee064670a460 100644
--- a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php
+++ b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php
@@ -97,7 +97,7 @@ public function __construct(
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function get(array $skus)
{
@@ -107,7 +107,7 @@ public function get(array $skus)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function update(array $prices)
{
@@ -128,7 +128,7 @@ public function update(array $prices)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function replace(array $prices)
{
@@ -144,7 +144,7 @@ public function replace(array $prices)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function delete(array $prices)
{
@@ -171,16 +171,17 @@ private function getExistingPrices(array $skus, $groupBySku = false)
$ids = $this->retrieveAffectedIds($skus);
$rawPrices = $this->tierPricePersistence->get($ids);
$prices = [];
-
- $linkField = $this->tierPricePersistence->getEntityLinkField();
- $skuByIdLookup = $this->buildSkuByIdLookup($skus);
- foreach ($rawPrices as $rawPrice) {
- $sku = $skuByIdLookup[$rawPrice[$linkField]];
- $price = $this->tierPriceFactory->create($rawPrice, $sku);
- if ($groupBySku) {
- $prices[$sku][] = $price;
- } else {
- $prices[] = $price;
+ if ($rawPrices) {
+ $linkField = $this->tierPricePersistence->getEntityLinkField();
+ $skuByIdLookup = $this->buildSkuByIdLookup($skus);
+ foreach ($rawPrices as $rawPrice) {
+ $sku = $skuByIdLookup[$rawPrice[$linkField]];
+ $price = $this->tierPriceFactory->create($rawPrice, $sku);
+ if ($groupBySku) {
+ $prices[$sku][] = $price;
+ } else {
+ $prices[] = $price;
+ }
}
}
diff --git a/app/code/Magento/Catalog/Model/ProductIdLocator.php b/app/code/Magento/Catalog/Model/ProductIdLocator.php
index 2d9af6829ad6e..2d382164f2649 100644
--- a/app/code/Magento/Catalog/Model/ProductIdLocator.php
+++ b/app/code/Magento/Catalog/Model/ProductIdLocator.php
@@ -37,23 +37,43 @@ class ProductIdLocator implements \Magento\Catalog\Model\ProductIdLocatorInterfa
*/
private $idsBySku = [];
+ /**
+ * Batch size to iterate collection
+ *
+ * @var int
+ */
+ private $batchSize;
+
/**
* @param \Magento\Framework\EntityManager\MetadataPool $metadataPool
* @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $collectionFactory
- * @param string $limitIdsBySkuValues
+ * @param string $idsLimit
+ * @param int $batchSize defines how many items can be processed by one iteration
*/
public function __construct(
\Magento\Framework\EntityManager\MetadataPool $metadataPool,
\Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $collectionFactory,
- $idsLimit
+ $idsLimit,
+ int $batchSize = 5000
) {
$this->metadataPool = $metadataPool;
$this->collectionFactory = $collectionFactory;
$this->idsLimit = (int)$idsLimit;
+ $this->batchSize = $batchSize;
}
/**
- * {@inheritdoc}
+ * @inheritdoc
+ *
+ * Load product items by provided products SKUs.
+ * Products collection will be iterated by pages with the $this->batchSize as a page size (for a cases when to many
+ * products SKUs were provided in parameters.
+ * Loaded products will be chached in the $this->idsBySku variable, but in the end of the method these storage will
+ * be truncated to $idsLimit quantity.
+ * As a result array with the products data will be returned with the following scheme:
+ * $data['product_sku']['link_field_value' => 'product_type']
+ *
+ * @throws \Exception
*/
public function retrieveProductIdsBySkus(array $skus)
{
@@ -72,8 +92,16 @@ public function retrieveProductIdsBySkus(array $skus)
$linkField = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class)
->getLinkField();
- foreach ($collection as $item) {
- $this->idsBySku[strtolower(trim($item->getSku()))][$item->getData($linkField)] = $item->getTypeId();
+ $collection->setPageSize($this->batchSize);
+ $pages = $collection->getLastPageNumber();
+ for ($currentPage = 1; $currentPage <= $pages; $currentPage++) {
+ $collection->setCurPage($currentPage);
+ foreach ($collection->getItems() as $item) {
+ $sku = strtolower(trim($item->getSku()));
+ $itemIdentifier = $item->getData($linkField);
+ $this->idsBySku[$sku][$itemIdentifier] = $item->getTypeId();
+ }
+ $collection->clear();
}
}
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category.php b/app/code/Magento/Catalog/Model/ResourceModel/Category.php
index 5b420c33b3184..90f55cd44bdb9 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Category.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Category.php
@@ -486,8 +486,20 @@ public function getProductsPosition($category)
$this->getCategoryProductTable(),
['product_id', 'position']
)->where(
- 'category_id = :category_id'
+ "{$this->getTable('catalog_category_product')}.category_id = ?",
+ $category->getId()
);
+ $websiteId = $category->getStore()->getWebsiteId();
+ if ($websiteId) {
+ $select->join(
+ ['product_website' => $this->getTable('catalog_product_website')],
+ "product_website.product_id = {$this->getTable('catalog_category_product')}.product_id",
+ []
+ )->where(
+ 'product_website.website_id = ?',
+ $websiteId
+ );
+ }
$bind = ['category_id' => (int)$category->getId()];
return $this->getConnection()->fetchPairs($select, $bind);
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php
index 4562aa0b0e75f..618abda0a942d 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php
@@ -322,7 +322,9 @@ public function loadProductCount($items, $countRegular = true, $countAnchor = tr
['e' => $this->getTable('catalog_category_entity')],
'main_table.category_id=e.entity_id',
[]
- )->where('e.entity_id = :entity_id OR e.path LIKE :c_path');
+ )->where(
+ '(e.entity_id = :entity_id OR e.path LIKE :c_path)'
+ );
if ($websiteId) {
$select->join(
['w' => $this->getProductWebsiteTable()],
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category/Flat.php b/app/code/Magento/Catalog/Model/ResourceModel/Category/Flat.php
index 9db2c8248ce52..05950531e2178 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Category/Flat.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Category/Flat.php
@@ -699,8 +699,20 @@ public function getProductsPosition($category)
$this->getTable('catalog_category_product'),
['product_id', 'position']
)->where(
- 'category_id = :category_id'
+ "{$this->getTable('catalog_category_product')}.category_id = ?",
+ $category->getId()
);
+ $websiteId = $category->getStore()->getWebsiteId();
+ if ($websiteId) {
+ $select->join(
+ ['product_website' => $this->getTable('catalog_product_website')],
+ "product_website.product_id = {$this->getTable('catalog_category_product')}.product_id",
+ []
+ )->where(
+ 'product_website.website_id = ?',
+ $websiteId
+ );
+ }
$bind = ['category_id' => (int)$category->getId()];
return $this->getConnection()->fetchPairs($select, $bind);
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/CategoryLink.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/CategoryLink.php
index f11c975396f2e..cf5760b0c33a9 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/CategoryLink.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/CategoryLink.php
@@ -118,9 +118,15 @@ private function processCategoryLinks($newCategoryPositions, &$oldCategoryPositi
$result = ['changed' => [], 'updated' => []];
$oldCategoryPositions = array_values($oldCategoryPositions);
- $oldCategoryList = array_column($oldCategoryPositions, 'category_id');
foreach ($newCategoryPositions as $newCategoryPosition) {
- $key = array_search($newCategoryPosition['category_id'], $oldCategoryList);
+ $key = false;
+
+ foreach ($oldCategoryPositions as $oldKey => $oldCategoryPosition) {
+ if ((int)$oldCategoryPosition['category_id'] === (int)$newCategoryPosition['category_id']) {
+ $key = $oldKey;
+ break;
+ }
+ }
if ($key === false) {
$result['changed'][] = $newCategoryPosition;
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
index 14ae38667d873..bd314c0192d38 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
@@ -290,6 +290,11 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac
*/
private $dimensionFactory;
+ /**
+ * @var \Magento\Framework\DataObject
+ */
+ private $emptyItem;
+
/**
* Collection constructor
* @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory
@@ -550,7 +555,10 @@ protected function _prepareStaticFields()
*/
public function getNewEmptyItem()
{
- $object = parent::getNewEmptyItem();
+ if (null === $this->emptyItem) {
+ $this->emptyItem = parent::getNewEmptyItem();
+ }
+ $object = clone $this->emptyItem;
if ($this->isEnabledFlat()) {
$object->setIdFieldName($this->getEntity()->getIdFieldName());
}
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignImageRolesActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignImageRolesActionGroup.xml
new file mode 100644
index 0000000000000..90ceb1e4a1f96
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignImageRolesActionGroup.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml
index 01b31430793cc..28587e6405e49 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml
@@ -70,6 +70,7 @@
+
@@ -121,7 +122,6 @@
-
@@ -313,4 +313,19 @@
+
+
+
+
+
+
+
+
+
+
+ {{amount}}
+ $grabProductTierPriceInput
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml
index fd1b6add8391f..817da18f5f2b7 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml
@@ -179,7 +179,6 @@
-
@@ -193,6 +192,17 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -233,6 +243,7 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenEditProductOnBackendActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenEditProductOnBackendActionGroup.xml
index 8f89a85e14892..ea2543cd5c2ab 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenEditProductOnBackendActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenEditProductOnBackendActionGroup.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml
index 5c79c321c9431..ff3c5cb4403bf 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml
@@ -42,4 +42,24 @@
true
+
+ FirstLevelSubCategory
+ subcategory
+
+
+ SecondLevelSubCategory
+ subcategory
+
+
+ ThirdLevelSubCategory
+ subcategory
+
+
+ FourthLevelSubCategory
+ subcategory
+
+
+ FifthLevelCategory
+ category
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml
index e5b23fe3a3c34..74492ed416206 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml
@@ -181,6 +181,9 @@
EavStockItem
CustomAttributeProductUrlKey
+
+ 1
+
Image1
1.00
@@ -477,4 +480,30 @@
EavStock1
CustomAttributeProductAttribute
+
+ Test
+ testsku
+ simple
+ 4
+ 4
+ 560.00
+ testurl-
+ 1
+ 25
+ 1
+ EavStock100
+
+
+ Product With Long Name And Sku - But not too long
+ Product With Long Name And Sku - But not too long
+
+
+ Magento3
+ 1.00
+ Upload File
+ Yes
+ magento3.jpg
+ magento3
+ jpg
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryPage.xml
index 9349e188430f4..f7d8abf8b2fea 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryPage.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryPage.xml
@@ -8,7 +8,7 @@
-
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml
index 9a0dd8f5b387d..651ee54c08339 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml
@@ -22,6 +22,7 @@
+
@@ -38,6 +39,13 @@
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml
index acee714fe3ec7..e3d224904671b 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml
@@ -16,5 +16,13 @@
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsGridSection.xml
index 40ea919226d6e..a449836fa08f4 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsGridSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsGridSection.xml
@@ -15,5 +15,6 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml
index 249610568aec7..5791d0bfedab9 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml
@@ -167,6 +167,12 @@
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml
index 59228d57502f1..fe87c81ad6ac8 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml
@@ -8,6 +8,7 @@
+
@@ -15,6 +16,7 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagesSection.xml
index eca0cb6f02ea1..89eb1ed678cc9 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagesSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagesSection.xml
@@ -16,6 +16,7 @@
+
@@ -32,4 +33,4 @@
-
\ No newline at end of file
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml
index 54b289a89c705..4dcda8dcd41ae 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml
@@ -11,5 +11,6 @@
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml
index 83c3ca5348606..45e0b03e8d995 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml
@@ -10,5 +10,6 @@
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml
index c87af1224ed30..e9c8f53f97e5f 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml
@@ -9,14 +9,18 @@
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml
index 36d9dfeedc5ec..545e7c10379bf 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml
@@ -269,4 +269,56 @@
grabTextFromMiniCartSubtotalField2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 100
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithAnchorFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithAnchorFieldTest.xml
new file mode 100644
index 0000000000000..9115004ad9585
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithAnchorFieldTest.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml
new file mode 100644
index 0000000000000..e8c6da476a3d6
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithFiveNestingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithFiveNestingTest.xml
new file mode 100644
index 0000000000000..530bafaef24c2
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithFiveNestingTest.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ['Home', {{FirstLevelSubCat.name}}, {{SecondLevelSubCat.name}}, {{ThirdLevelSubCat.name}}, {{FourthLevelSubCat.name}}, {{FifthLevelCat.name}} ]
+ breadcrumbs
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveCategoryTest.xml
new file mode 100644
index 0000000000000..96f945da138b0
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveCategoryTest.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveIncludeInMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveIncludeInMenuTest.xml
new file mode 100644
index 0000000000000..c983089163f78
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveIncludeInMenuTest.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilter.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilter.xml
new file mode 100644
index 0000000000000..7c24a8aba27bd
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilter.xml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithRequiredFieldsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithRequiredFieldsTest.xml
new file mode 100644
index 0000000000000..1b6c9707b0656
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithRequiredFieldsTest.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryRequiredFieldsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryRequiredFieldsTest.xml
new file mode 100644
index 0000000000000..f98f9acc46961
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryRequiredFieldsTest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml
index 9e323745835c2..e0e214342ad72 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml
@@ -19,19 +19,19 @@
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
+
+
+
@@ -50,54 +50,46 @@
-
-
+
+
+
+
+
-
-
-
-
-
-
+
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
+
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
+
-
+
@@ -109,10 +101,13 @@
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
@@ -146,21 +137,16 @@
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 100
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml
index 281bad4e49591..7da668df59022 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml
@@ -108,6 +108,10 @@
+
+
+
+
@@ -147,9 +151,9 @@
+
-
-
+
@@ -318,13 +322,17 @@
-
-
-
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml
similarity index 92%
rename from app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml
rename to app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml
index c845a27773170..d96399738c80a 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml
@@ -8,7 +8,7 @@
-
+
@@ -17,6 +17,8 @@
+
+
@@ -53,6 +55,10 @@
+
+
+
+
@@ -64,10 +70,10 @@
-
+
-
+
@@ -100,8 +106,8 @@
-
-
+
+
@@ -138,8 +144,8 @@
-
-
+
+
@@ -156,8 +162,8 @@
-
-
+
+
@@ -174,8 +180,8 @@
-
-
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml
index fcdb92d71f1f8..04cb813ec0efb 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml
@@ -33,6 +33,8 @@
+
+
@@ -78,6 +80,9 @@
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnProductTest.xml
index 26dd94b19de64..29ed3af4f01d9 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnProductTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnProductTest.xml
@@ -32,16 +32,19 @@
-
-
+
+
+
+
+
-
+
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php
index da6b790fedfa6..7c2ec8abb768a 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php
@@ -18,6 +18,11 @@ class DefaultValidatorTest extends \PHPUnit\Framework\TestCase
*/
protected $valueMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $localeFormatMock;
+
/**
* @inheritdoc
*/
@@ -26,6 +31,8 @@ protected function setUp()
$configMock = $this->createMock(\Magento\Catalog\Model\ProductOptions\ConfigInterface::class);
$storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class);
$priceConfigMock = new \Magento\Catalog\Model\Config\Source\Product\Options\Price($storeManagerMock);
+ $this->localeFormatMock = $this->createMock(\Magento\Framework\Locale\FormatInterface::class);
+
$config = [
[
'label' => 'group label 1',
@@ -51,7 +58,8 @@ protected function setUp()
$configMock->expects($this->once())->method('getAll')->will($this->returnValue($config));
$this->validator = new \Magento\Catalog\Model\Product\Option\Validator\DefaultValidator(
$configMock,
- $priceConfigMock
+ $priceConfigMock,
+ $this->localeFormatMock
);
}
@@ -63,10 +71,10 @@ public function isValidTitleDataProvider()
{
$mess = ['option required fields' => 'Missed values for option required fields'];
return [
- ['option_title', 'name 1.1', 'fixed', new \Magento\Framework\DataObject(['store_id' => 1]), [], true],
- ['option_title', 'name 1.1', 'fixed', new \Magento\Framework\DataObject(['store_id' => 0]), [], true],
- [null, 'name 1.1', 'fixed', new \Magento\Framework\DataObject(['store_id' => 1]), [], true],
- [null, 'name 1.1', 'fixed', new \Magento\Framework\DataObject(['store_id' => 0]), $mess, false],
+ ['option_title', 'name 1.1', 'fixed', 10, new \Magento\Framework\DataObject(['store_id' => 1]), [], true],
+ ['option_title', 'name 1.1', 'fixed', 10, new \Magento\Framework\DataObject(['store_id' => 0]), [], true],
+ [null, 'name 1.1', 'fixed', 10, new \Magento\Framework\DataObject(['store_id' => 1]), [], true],
+ [null, 'name 1.1', 'fixed', 10, new \Magento\Framework\DataObject(['store_id' => 0]), $mess, false],
];
}
@@ -79,15 +87,18 @@ public function isValidTitleDataProvider()
* @param bool $result
* @dataProvider isValidTitleDataProvider
*/
- public function testIsValidTitle($title, $type, $priceType, $product, $messages, $result)
+ public function testIsValidTitle($title, $type, $priceType, $price, $product, $messages, $result)
{
- $methods = ['getTitle', 'getType', 'getPriceType', '__wakeup', 'getProduct'];
+ $methods = ['getTitle', 'getType', 'getPriceType', 'getPrice', '__wakeup', 'getProduct'];
$valueMock = $this->createPartialMock(\Magento\Catalog\Model\Product\Option::class, $methods);
$valueMock->expects($this->once())->method('getTitle')->will($this->returnValue($title));
$valueMock->expects($this->any())->method('getType')->will($this->returnValue($type));
$valueMock->expects($this->once())->method('getPriceType')->will($this->returnValue($priceType));
- // $valueMock->expects($this->once())->method('getPrice')->will($this->returnValue($price));
+ $valueMock->expects($this->once())->method('getPrice')->will($this->returnValue($price));
$valueMock->expects($this->once())->method('getProduct')->will($this->returnValue($product));
+
+ $this->localeFormatMock->expects($this->once())->method('getNumber')->will($this->returnValue($price));
+
$this->assertEquals($result, $this->validator->isValid($valueMock));
$this->assertEquals($messages, $this->validator->getMessages());
}
@@ -126,4 +137,43 @@ public function testIsValidFail($product)
$this->assertFalse($this->validator->isValid($valueMock));
$this->assertEquals($messages, $this->validator->getMessages());
}
+
+ /**
+ * Data provider for testValidationNegativePrice
+ * @return array
+ */
+ public function validationPriceDataProvider()
+ {
+ return [
+ ['option_title', 'name 1.1', 'fixed', -12, new \Magento\Framework\DataObject(['store_id' => 1])],
+ ['option_title', 'name 1.1', 'fixed', -12, new \Magento\Framework\DataObject(['store_id' => 0])],
+ ['option_title', 'name 1.1', 'fixed', 12, new \Magento\Framework\DataObject(['store_id' => 1])],
+ ['option_title', 'name 1.1', 'fixed', 12, new \Magento\Framework\DataObject(['store_id' => 0])]
+ ];
+ }
+
+ /**
+ * @param $title
+ * @param $type
+ * @param $priceType
+ * @param $price
+ * @param $product
+ * @dataProvider validationPriceDataProvider
+ */
+ public function testValidationPrice($title, $type, $priceType, $price, $product)
+ {
+ $methods = ['getTitle', 'getType', 'getPriceType', 'getPrice', '__wakeup', 'getProduct'];
+ $valueMock = $this->createPartialMock(\Magento\Catalog\Model\Product\Option::class, $methods);
+ $valueMock->expects($this->once())->method('getTitle')->will($this->returnValue($title));
+ $valueMock->expects($this->exactly(2))->method('getType')->will($this->returnValue($type));
+ $valueMock->expects($this->once())->method('getPriceType')->will($this->returnValue($priceType));
+ $valueMock->expects($this->once())->method('getPrice')->will($this->returnValue($price));
+ $valueMock->expects($this->once())->method('getProduct')->will($this->returnValue($product));
+
+ $this->localeFormatMock->expects($this->once())->method('getNumber')->will($this->returnValue($price));
+
+ $messages = [];
+ $this->assertTrue($this->validator->isValid($valueMock));
+ $this->assertEquals($messages, $this->validator->getMessages());
+ }
}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php
index 2de993c075514..e688da1c6aa16 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php
@@ -18,6 +18,11 @@ class FileTest extends \PHPUnit\Framework\TestCase
*/
protected $valueMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $localeFormatMock;
+
/**
* @inheritdoc
*/
@@ -26,6 +31,8 @@ protected function setUp()
$configMock = $this->createMock(\Magento\Catalog\Model\ProductOptions\ConfigInterface::class);
$storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class);
$priceConfigMock = new \Magento\Catalog\Model\Config\Source\Product\Options\Price($storeManagerMock);
+ $this->localeFormatMock = $this->createMock(\Magento\Framework\Locale\FormatInterface::class);
+
$config = [
[
'label' => 'group label 1',
@@ -53,7 +60,8 @@ protected function setUp()
$this->valueMock = $this->createPartialMock(\Magento\Catalog\Model\Product\Option::class, $methods);
$this->validator = new \Magento\Catalog\Model\Product\Option\Validator\File(
$configMock,
- $priceConfigMock
+ $priceConfigMock,
+ $this->localeFormatMock
);
}
@@ -70,6 +78,15 @@ public function testIsValidSuccess()
->willReturn(10);
$this->valueMock->expects($this->once())->method('getImageSizeX')->will($this->returnValue(10));
$this->valueMock->expects($this->once())->method('getImageSizeY')->will($this->returnValue(15));
+ $this->localeFormatMock->expects($this->at(0))
+ ->method('getNumber')
+ ->with($this->equalTo(10))
+ ->will($this->returnValue(10));
+ $this->localeFormatMock
+ ->expects($this->at(2))
+ ->method('getNumber')
+ ->with($this->equalTo(15))
+ ->will($this->returnValue(15));
$this->assertEmpty($this->validator->getMessages());
$this->assertTrue($this->validator->isValid($this->valueMock));
}
@@ -87,6 +104,16 @@ public function testIsValidWithNegativeImageSize()
->willReturn(10);
$this->valueMock->expects($this->once())->method('getImageSizeX')->will($this->returnValue(-10));
$this->valueMock->expects($this->never())->method('getImageSizeY');
+ $this->localeFormatMock->expects($this->at(0))
+ ->method('getNumber')
+ ->with($this->equalTo(10))
+ ->will($this->returnValue(10));
+ $this->localeFormatMock
+ ->expects($this->at(1))
+ ->method('getNumber')
+ ->with($this->equalTo(-10))
+ ->will($this->returnValue(-10));
+
$messages = [
'option values' => 'Invalid option value',
];
@@ -107,6 +134,15 @@ public function testIsValidWithNegativeImageSizeY()
->willReturn(10);
$this->valueMock->expects($this->once())->method('getImageSizeX')->will($this->returnValue(10));
$this->valueMock->expects($this->once())->method('getImageSizeY')->will($this->returnValue(-10));
+ $this->localeFormatMock->expects($this->at(0))
+ ->method('getNumber')
+ ->with($this->equalTo(10))
+ ->will($this->returnValue(10));
+ $this->localeFormatMock
+ ->expects($this->at(2))
+ ->method('getNumber')
+ ->with($this->equalTo(-10))
+ ->will($this->returnValue(-10));
$messages = [
'option values' => 'Invalid option value',
];
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php
index b97783edf856c..7fad5592a2d21 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php
@@ -18,6 +18,11 @@ class SelectTest extends \PHPUnit\Framework\TestCase
*/
protected $valueMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $localeFormatMock;
+
/**
* @inheritdoc
*/
@@ -26,6 +31,7 @@ protected function setUp()
$configMock = $this->createMock(\Magento\Catalog\Model\ProductOptions\ConfigInterface::class);
$storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class);
$priceConfigMock = new \Magento\Catalog\Model\Config\Source\Product\Options\Price($storeManagerMock);
+ $this->localeFormatMock = $this->createMock(\Magento\Framework\Locale\FormatInterface::class);
$config = [
[
'label' => 'group label 1',
@@ -53,7 +59,8 @@ protected function setUp()
$this->valueMock = $this->createPartialMock(\Magento\Catalog\Model\Product\Option::class, $methods, []);
$this->validator = new \Magento\Catalog\Model\Product\Option\Validator\Select(
$configMock,
- $priceConfigMock
+ $priceConfigMock,
+ $this->localeFormatMock
);
}
@@ -69,6 +76,12 @@ public function testIsValidSuccess($expectedResult, array $value)
$this->valueMock->expects($this->never())->method('getPriceType');
$this->valueMock->expects($this->never())->method('getPrice');
$this->valueMock->expects($this->any())->method('getData')->with('values')->will($this->returnValue([$value]));
+ if (isset($value['price'])) {
+ $this->localeFormatMock
+ ->expects($this->once())
+ ->method('getNumber')
+ ->will($this->returnValue($value['price']));
+ }
$this->assertEquals($expectedResult, $this->validator->isValid($this->valueMock));
}
@@ -117,6 +130,7 @@ public function testIsValidateWithInvalidOptionValues()
->method('getData')
->with('values')
->will($this->returnValue('invalid_data'));
+
$messages = [
'option values' => 'Invalid option value',
];
@@ -159,6 +173,7 @@ public function testIsValidateWithInvalidData($priceType, $price, $title)
$this->valueMock->expects($this->never())->method('getPriceType');
$this->valueMock->expects($this->never())->method('getPrice');
$this->valueMock->expects($this->any())->method('getData')->with('values')->will($this->returnValue([$value]));
+ $this->localeFormatMock->expects($this->any())->method('getNumber')->will($this->returnValue($price));
$messages = [
'option values' => 'Invalid option value',
];
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php
index 4881154728ddc..a3e6189f74925 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php
@@ -18,6 +18,11 @@ class TextTest extends \PHPUnit\Framework\TestCase
*/
protected $valueMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $localeFormatMock;
+
/**
* @inheritdoc
*/
@@ -26,6 +31,7 @@ protected function setUp()
$configMock = $this->createMock(\Magento\Catalog\Model\ProductOptions\ConfigInterface::class);
$storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class);
$priceConfigMock = new \Magento\Catalog\Model\Config\Source\Product\Options\Price($storeManagerMock);
+ $this->localeFormatMock = $this->createMock(\Magento\Framework\Locale\FormatInterface::class);
$config = [
[
'label' => 'group label 1',
@@ -53,7 +59,8 @@ protected function setUp()
$this->valueMock = $this->createPartialMock(\Magento\Catalog\Model\Product\Option::class, $methods);
$this->validator = new \Magento\Catalog\Model\Product\Option\Validator\Text(
$configMock,
- $priceConfigMock
+ $priceConfigMock,
+ $this->localeFormatMock
);
}
@@ -69,6 +76,10 @@ public function testIsValidSuccess()
$this->valueMock->method('getPrice')
->willReturn(10);
$this->valueMock->expects($this->once())->method('getMaxCharacters')->will($this->returnValue(10));
+ $this->localeFormatMock->expects($this->exactly(2))
+ ->method('getNumber')
+ ->with($this->equalTo(10))
+ ->will($this->returnValue(10));
$this->assertTrue($this->validator->isValid($this->valueMock));
$this->assertEmpty($this->validator->getMessages());
}
@@ -85,6 +96,15 @@ public function testIsValidWithNegativeMaxCharacters()
$this->valueMock->method('getPrice')
->willReturn(10);
$this->valueMock->expects($this->once())->method('getMaxCharacters')->will($this->returnValue(-10));
+ $this->localeFormatMock->expects($this->at(0))
+ ->method('getNumber')
+ ->with($this->equalTo(10))
+ ->will($this->returnValue(10));
+ $this->localeFormatMock
+ ->expects($this->at(1))
+ ->method('getNumber')
+ ->with($this->equalTo(-10))
+ ->will($this->returnValue(-10));
$messages = [
'option values' => 'Invalid option value',
];
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php
index c9288790ed6e1..a97f2281125a6 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php
@@ -152,6 +152,30 @@ public function testGet()
$this->assertEquals(2, count($prices));
}
+ /**
+ * Test get method without tierprices.
+ *
+ * @return void
+ */
+ public function testGetWithoutTierPrices()
+ {
+ $skus = ['simple', 'virtual'];
+ $this->tierPriceValidator
+ ->expects($this->once())
+ ->method('validateSkus')
+ ->with($skus)
+ ->willReturn($skus);
+ $this->productIdLocator->expects($this->atLeastOnce())
+ ->method('retrieveProductIdsBySkus')
+ ->with(['simple', 'virtual'])
+ ->willReturn(['simple' => ['2' => 'simple'], 'virtual' => ['3' => 'virtual']]);
+ $this->tierPricePersistence->expects($this->once())->method('get')->willReturn([]);
+ $this->tierPricePersistence->expects($this->never())->method('getEntityLinkField');
+ $this->tierPriceFactory->expects($this->never())->method('create');
+ $prices = $this->tierPriceStorage->get($skus);
+ $this->assertEmpty($prices);
+ }
+
/**
* Test update method.
*
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php
index b730e12ca820b..b9cb82274c808 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php
@@ -58,7 +58,16 @@ public function testRetrieveProductIdsBySkus()
{
$skus = ['sku_1', 'sku_2'];
$collection = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product\Collection::class)
- ->setMethods(['getIterator', 'addFieldToFilter'])
+ ->setMethods(
+ [
+ 'getItems',
+ 'addFieldToFilter',
+ 'setPageSize',
+ 'getLastPageNumber',
+ 'setCurPage',
+ 'clear'
+ ]
+ )
->disableOriginalConstructor()->getMock();
$product = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class)
->setMethods(['getSku', 'getData', 'getTypeId'])
@@ -69,7 +78,11 @@ public function testRetrieveProductIdsBySkus()
$this->collectionFactory->expects($this->once())->method('create')->willReturn($collection);
$collection->expects($this->once())->method('addFieldToFilter')
->with(\Magento\Catalog\Api\Data\ProductInterface::SKU, ['in' => $skus])->willReturnSelf();
- $collection->expects($this->once())->method('getIterator')->willReturn(new \ArrayIterator([$product]));
+ $collection->expects($this->atLeastOnce())->method('getItems')->willReturn([$product]);
+ $collection->expects($this->atLeastOnce())->method('setPageSize')->willReturnSelf();
+ $collection->expects($this->atLeastOnce())->method('getLastPageNumber')->willReturn(1);
+ $collection->expects($this->atLeastOnce())->method('setCurPage')->with(1)->willReturnSelf();
+ $collection->expects($this->atLeastOnce())->method('clear')->willReturnSelf();
$this->metadataPool
->expects($this->once())
->method('getMetadata')
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php
index c95b812705adc..9867fdf910219 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php
@@ -549,6 +549,7 @@ public function testSetCategoryCollection()
public function testGetCategory()
{
+ $this->model->setData('category_ids', [10]);
$this->category->expects($this->any())->method('getId')->will($this->returnValue(10));
$this->registry->expects($this->any())->method('registry')->will($this->returnValue($this->category));
$this->categoryRepository->expects($this->any())->method('get')->will($this->returnValue($this->category));
@@ -557,7 +558,8 @@ public function testGetCategory()
public function testGetCategoryId()
{
- $this->category->expects($this->once())->method('getId')->will($this->returnValue(10));
+ $this->model->setData('category_ids', [10]);
+ $this->category->expects($this->any())->method('getId')->will($this->returnValue(10));
$this->registry->expects($this->at(0))->method('registry');
$this->registry->expects($this->at(1))->method('registry')->will($this->returnValue($this->category));
@@ -565,6 +567,14 @@ public function testGetCategoryId()
$this->assertEquals(10, $this->model->getCategoryId());
}
+ public function testGetCategoryIdWhenProductNotInCurrentCategory()
+ {
+ $this->model->setData('category_ids', [12]);
+ $this->category->expects($this->once())->method('getId')->will($this->returnValue(10));
+ $this->registry->expects($this->any())->method('registry')->will($this->returnValue($this->category));
+ $this->assertFalse($this->model->getCategoryId());
+ }
+
public function testGetIdBySku()
{
$this->resource->expects($this->once())->method('getIdBySku')->will($this->returnValue(5));
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CategoryLinkTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CategoryLinkTest.php
index 9e2b196602993..5a1a5906ec4b9 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CategoryLinkTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CategoryLinkTest.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Catalog\Test\Unit\Model\ResourceModel\Product;
use Magento\Catalog\Model\ResourceModel\Product\CategoryLink;
@@ -129,9 +130,20 @@ public function testSaveCategoryLinks($newCategoryLinks, $dbCategoryLinks, $affe
);
}
+ $expectedResult = [];
+
+ foreach ($affectedIds as $type => $ids) {
+ $expectedResult = array_merge($expectedResult, $ids);
+ // Verify if the correct insert, update and/or delete actions are performed:
+ $this->setupExpectationsForConnection($type, $ids);
+ }
+
$actualResult = $this->model->saveCategoryLinks($product, $newCategoryLinks);
+
sort($actualResult);
- $this->assertEquals($affectedIds, $actualResult);
+ sort($expectedResult);
+
+ $this->assertEquals($expectedResult, $actualResult);
}
/**
@@ -151,7 +163,11 @@ public function getCategoryLinksDataProvider()
['category_id' => 3, 'position' => 10],
['category_id' => 4, 'position' => 20],
],
- [], // Nothing to update - data not changed
+ [
+ 'update' => [],
+ 'insert' => [],
+ 'delete' => [],
+ ],
],
[
[
@@ -162,7 +178,11 @@ public function getCategoryLinksDataProvider()
['category_id' => 3, 'position' => 10],
['category_id' => 4, 'position' => 20],
],
- [3, 4, 5], // 4 - updated position, 5 - added, 3 - deleted
+ [
+ 'update' => [4],
+ 'insert' => [5],
+ 'delete' => [3],
+ ],
],
[
[
@@ -173,7 +193,11 @@ public function getCategoryLinksDataProvider()
['category_id' => 3, 'position' => 10],
['category_id' => 4, 'position' => 20],
],
- [3, 4], // 3 - updated position, 4 - deleted
+ [
+ 'update' => [3],
+ 'insert' => [],
+ 'delete' => [4],
+ ],
],
[
[],
@@ -181,8 +205,80 @@ public function getCategoryLinksDataProvider()
['category_id' => 3, 'position' => 10],
['category_id' => 4, 'position' => 20],
],
- [3, 4], // 3, 4 - deleted
+ [
+ 'update' => [],
+ 'insert' => [],
+ 'delete' => [3, 4],
+ ],
],
+ [
+ [
+ ['category_id' => 3, 'position' => 10],
+ ['category_id' => 4, 'position' => 20],
+ ],
+ [
+ ['category_id' => 3, 'position' => 20], // swapped positions
+ ['category_id' => 4, 'position' => 10], // swapped positions
+ ],
+ [
+ 'update' => [3, 4],
+ 'insert' => [],
+ 'delete' => [],
+ ],
+ ]
];
}
+
+ /**
+ * @param $type
+ * @param $ids
+ */
+ private function setupExpectationsForConnection($type, $ids): void
+ {
+ switch ($type) {
+ case 'insert':
+ $this->connectionMock
+ ->expects($this->exactly(empty($ids) ? 0 : 1))
+ ->method('insertArray')
+ ->with(
+ $this->anything(),
+ $this->anything(),
+ $this->callback(function ($data) use ($ids) {
+ $foundIds = [];
+ foreach ($data as $row) {
+ $foundIds[] = $row['category_id'];
+ }
+ return $ids === $foundIds;
+ })
+ );
+ break;
+ case 'update':
+ $this->connectionMock
+ ->expects($this->exactly(empty($ids) ? 0 : 1))
+ ->method('insertOnDuplicate')
+ ->with(
+ $this->anything(),
+ $this->callback(function ($data) use ($ids) {
+ $foundIds = [];
+ foreach ($data as $row) {
+ $foundIds[] = $row['category_id'];
+ }
+ return $ids === $foundIds;
+ })
+ );
+ break;
+ case 'delete':
+ $this->connectionMock
+ ->expects($this->exactly(empty($ids) ? 0 : 1))
+ ->method('delete')
+ // Verify that the correct category ID's are touched:
+ ->with(
+ $this->anything(),
+ $this->callback(function ($data) use ($ids) {
+ return array_values($data)[1] === $ids;
+ })
+ );
+ break;
+ }
+ }
}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php
index dbbb3fb29513b..bb39aa7f9db77 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php
@@ -58,13 +58,18 @@ class CollectionTest extends \PHPUnit\Framework\TestCase
*/
private $storeManager;
+ /**
+ * @var \Magento\Framework\Data\Collection\EntityFactory|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $entityFactory;
+
/**
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
protected function setUp()
{
$this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
- $entityFactory = $this->createMock(\Magento\Framework\Data\Collection\EntityFactory::class);
+ $this->entityFactory = $this->createMock(\Magento\Framework\Data\Collection\EntityFactory::class);
$logger = $this->getMockBuilder(\Psr\Log\LoggerInterface::class)
->disableOriginalConstructor()
->getMockForAbstractClass();
@@ -168,7 +173,7 @@ protected function setUp()
$this->collection = $this->objectManager->getObject(
\Magento\Catalog\Model\ResourceModel\Product\Collection::class,
[
- 'entityFactory' => $entityFactory,
+ 'entityFactory' => $this->entityFactory,
'logger' => $logger,
'fetchStrategy' => $fetchStrategy,
'eventManager' => $eventManager,
@@ -379,4 +384,20 @@ public function testAddTierPriceData()
$this->assertSame($this->collection, $this->collection->addTierPriceData());
}
+
+ /**
+ * Test for getNewEmptyItem() method
+ *
+ * @return void
+ */
+ public function testGetNewEmptyItem()
+ {
+ $item = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->entityFactory->expects($this->once())->method('create')->willReturn($item);
+ $firstItem = $this->collection->getNewEmptyItem();
+ $secondItem = $this->collection->getNewEmptyItem();
+ $this->assertEquals($firstItem, $secondItem);
+ }
}
diff --git a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php
index dbf1292e57368..a4ccaffc8fb6a 100644
--- a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php
@@ -152,21 +152,21 @@ public function productJsonEncodeDataProvider() : array
return [
[
$this->getObjectManager()->getObject(Product::class, ['data' => ['name' => 'Test ™']]),
- '{"breadcrumbs":{"categoryUrlSuffix":"."html","userCategoryPathInUrl":0,"product":"Test \u2122"}}',
+ '{"breadcrumbs":{"categoryUrlSuffix":"."html","useCategoryPathInUrl":0,"product":"Test \u2122"}}',
],
[
$this->getObjectManager()->getObject(Product::class, ['data' => ['name' => 'Test "']]),
- '{"breadcrumbs":{"categoryUrlSuffix":"."html","userCategoryPathInUrl":0,"product":"Test ""}}',
+ '{"breadcrumbs":{"categoryUrlSuffix":"."html","useCategoryPathInUrl":0,"product":"Test ""}}',
],
[
$this->getObjectManager()->getObject(Product::class, ['data' => ['name' => 'Test x ']]),
- '{"breadcrumbs":{"categoryUrlSuffix":"."html","userCategoryPathInUrl":0,"product":'
+ '{"breadcrumbs":{"categoryUrlSuffix":"."html","useCategoryPathInUrl":0,"product":'
. '"Test <b>x<\/b>"}}',
],
[
$this->getObjectManager()->getObject(Product::class, ['data' => ['name' => 'Test \'abc\'']]),
'{"breadcrumbs":'
- . '{"categoryUrlSuffix":"."html","userCategoryPathInUrl":0,"product":"Test 'abc'"}}'
+ . '{"categoryUrlSuffix":"."html","useCategoryPathInUrl":0,"product":"Test 'abc'"}}'
],
];
}
diff --git a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php
index cbc67fee8a5a3..1903bcd144831 100644
--- a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php
+++ b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php
@@ -6,6 +6,8 @@
namespace Magento\Catalog\Ui\Component;
/**
+ * Column Factory
+ *
* @api
* @since 100.0.2
*/
@@ -47,10 +49,14 @@ public function __construct(\Magento\Framework\View\Element\UiComponentFactory $
}
/**
+ * Create Factory
+ *
* @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute
* @param \Magento\Framework\View\Element\UiComponent\ContextInterface $context
* @param array $config
+ *
* @return \Magento\Ui\Component\Listing\Columns\ColumnInterface
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function create($attribute, $context, array $config = [])
{
@@ -82,7 +88,10 @@ public function create($attribute, $context, array $config = [])
}
/**
+ * Get Js Component
+ *
* @param string $dataType
+ *
* @return string
*/
protected function getJsComponent($dataType)
@@ -91,14 +100,15 @@ protected function getJsComponent($dataType)
}
/**
+ * Get Data Type
+ *
* @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute
+ *
* @return string
*/
protected function getDataType($attribute)
{
- return isset($this->dataTypeMap[$attribute->getFrontendInput()])
- ? $this->dataTypeMap[$attribute->getFrontendInput()]
- : $this->dataTypeMap['default'];
+ return $this->dataTypeMap[$attribute->getFrontendInput()] ?? $this->dataTypeMap['default'];
}
/**
@@ -111,6 +121,6 @@ protected function getFilterType($frontendInput)
{
$filtersMap = ['date' => 'dateRange'];
$result = array_replace_recursive($this->dataTypeMap, $filtersMap);
- return isset($result[$frontendInput]) ? $result[$frontendInput] : $result['default'];
+ return $result[$frontendInput] ?? $result['default'];
}
}
diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Thumbnail.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Thumbnail.php
index d4dc9ddd7ca3b..09c9782fc0e32 100644
--- a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Thumbnail.php
+++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Thumbnail.php
@@ -9,6 +9,8 @@
use Magento\Framework\View\Element\UiComponent\ContextInterface;
/**
+ * Class Thumbnail
+ *
* @api
* @since 100.0.2
*/
@@ -67,6 +69,8 @@ public function prepareDataSource(array $dataSource)
}
/**
+ * Get Alt
+ *
* @param array $row
*
* @return null|string
@@ -74,6 +78,6 @@ public function prepareDataSource(array $dataSource)
protected function getAlt($row)
{
$altField = $this->getData('config/altField') ?: self::ALT_FIELD;
- return isset($row[$altField]) ? $row[$altField] : null;
+ return $row[$altField] ?? null;
}
}
diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php
index 524927ac1c4b4..4fcb87ab1396e 100644
--- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php
+++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php
@@ -14,6 +14,7 @@
use Magento\Catalog\Model\Product\Image\NotLoadInfoImageException;
use Magento\Catalog\Ui\DataProvider\Product\ProductRenderCollectorInterface;
use Magento\Framework\App\State;
+use Magento\Framework\View\Design\ThemeInterface;
use Magento\Framework\View\DesignInterface;
use Magento\Store\Model\StoreManager;
use Magento\Store\Model\StoreManagerInterface;
@@ -104,6 +105,8 @@ public function __construct(
public function collect(ProductInterface $product, ProductRenderInterface $productRender)
{
$images = [];
+ /** @var ThemeInterface $currentTheme */
+ $currentTheme = $this->design->getDesignTheme();
foreach ($this->imageCodes as $imageCode) {
/** @var ImageInterface $image */
@@ -132,6 +135,7 @@ public function collect(ProductInterface $product, ProductRenderInterface $produ
$images[] = $image;
}
+ $this->design->setDesignTheme($currentTheme);
$productRender->setImages($images);
}
diff --git a/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php b/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php
index 95f2531e5fdca..d1424d637937b 100644
--- a/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php
+++ b/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php
@@ -105,7 +105,7 @@ public function getJsonConfigurationHtmlEscaped() : string
[
'breadcrumbs' => [
'categoryUrlSuffix' => $this->escaper->escapeHtml($this->getCategoryUrlSuffix()),
- 'userCategoryPathInUrl' => (int)$this->isCategoryUsedInProductUrl(),
+ 'useCategoryPathInUrl' => (int)$this->isCategoryUsedInProductUrl(),
'product' => $this->escaper->escapeHtml($this->getProductName())
]
],
diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_crosssell.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_crosssell.xml
index 4a27158be5f7c..d7aa058a1f446 100644
--- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_crosssell.xml
+++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_crosssell.xml
@@ -5,6 +5,10 @@
* See COPYING.txt for license details.
*/
-->
+
diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_crosssellgrid.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_crosssellgrid.xml
index b5efecf0d03c2..3ba4562c9d3df 100644
--- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_crosssellgrid.xml
+++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_crosssellgrid.xml
@@ -5,6 +5,10 @@
* See COPYING.txt for license details.
*/
-->
+
diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_related.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_related.xml
index 1340d40ef4f9f..c40c4a2818efa 100644
--- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_related.xml
+++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_related.xml
@@ -5,6 +5,10 @@
* See COPYING.txt for license details.
*/
-->
+
diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_relatedgrid.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_relatedgrid.xml
index 1ae83419ae646..38b791d88a00d 100644
--- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_relatedgrid.xml
+++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_relatedgrid.xml
@@ -5,6 +5,10 @@
* See COPYING.txt for license details.
*/
-->
+
diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_upsell.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_upsell.xml
index a9770ed3c182e..eea4450411945 100644
--- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_upsell.xml
+++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_upsell.xml
@@ -5,6 +5,10 @@
* See COPYING.txt for license details.
*/
-->
+
diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_upsellgrid.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_upsellgrid.xml
index a4acf572caeb5..2c400746c64f2 100644
--- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_upsellgrid.xml
+++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_upsellgrid.xml
@@ -5,6 +5,10 @@
* See COPYING.txt for license details.
*/
-->
+
diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/tree.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/tree.phtml
index 9865589556e7b..93666470b1b2c 100644
--- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/tree.phtml
+++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/tree.phtml
@@ -248,7 +248,7 @@
try {
response = JSON.parse(transport.responseText);
} catch (e) {
- console.warn('An error occured while parsing response');
+ console.warn('An error occurred while parsing response');
}
if (!response || !response['parameters']) {
diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml
index 07a801f42a786..195ac92422715 100644
--- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml
+++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml
@@ -334,7 +334,7 @@ if ($('is_required')) {
jQuery(function($) {
bindAttributeInputType();
- // @todo: refactor collapsable component
+ // @todo: refactor collapsible component
$('.attribute-popup .collapse, [data-role="advanced_fieldset-content"]')
.collapsable()
.collapse('hide');
diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml
index 670a943da0aad..9621b9a57168c 100644
--- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml
+++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml
@@ -80,7 +80,7 @@
// set the root node
this.root = new Ext.tree.TreeNode({
text: 'ROOT',
- allowDrug:false,
+ allowDrag:false,
allowDrop:true,
id:'1'
});
diff --git a/app/code/Magento/Catalog/view/adminhtml/web/catalog/product/composite/configure.js b/app/code/Magento/Catalog/view/adminhtml/web/catalog/product/composite/configure.js
index a2804a8723ce0..1ac2a4ffadaae 100644
--- a/app/code/Magento/Catalog/view/adminhtml/web/catalog/product/composite/configure.js
+++ b/app/code/Magento/Catalog/view/adminhtml/web/catalog/product/composite/configure.js
@@ -91,8 +91,8 @@ define([
/**
* Add product list types as scope and their urls
- * expamle: addListType('product_to_add', {urlFetch: 'http://magento...'})
- * expamle: addListType('wishlist', {urlSubmit: 'http://magento...'})
+ * example: addListType('product_to_add', {urlFetch: 'http://magento...'})
+ * example: addListType('wishlist', {urlSubmit: 'http://magento...'})
*
* @param type types as scope
* @param urls obj can be
@@ -112,7 +112,7 @@ define([
/**
* Adds complex list type - that is used to submit several list types at once
* Only urlSubmit is possible for this list type
- * expamle: addComplexListType(['wishlist', 'product_list'], 'http://magento...')
+ * example: addComplexListType(['wishlist', 'product_list'], 'http://magento...')
*
* @param type types as scope
* @param urls obj can be
diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/form/element/action-delete.js b/app/code/Magento/Catalog/view/adminhtml/web/js/form/element/action-delete.js
index 97f978de47b60..f829c66c4011c 100644
--- a/app/code/Magento/Catalog/view/adminhtml/web/js/form/element/action-delete.js
+++ b/app/code/Magento/Catalog/view/adminhtml/web/js/form/element/action-delete.js
@@ -5,10 +5,10 @@
define([
'underscore',
'Magento_Ui/js/form/element/abstract'
-], function (_, Acstract) {
+], function (_, Abstract) {
'use strict';
- return Acstract.extend({
+ return Abstract.extend({
defaults: {
prefixName: '',
prefixElementName: '',
diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/form/element/input.js b/app/code/Magento/Catalog/view/adminhtml/web/js/form/element/input.js
index 2f6703cc92eac..4bbdea066b762 100644
--- a/app/code/Magento/Catalog/view/adminhtml/web/js/form/element/input.js
+++ b/app/code/Magento/Catalog/view/adminhtml/web/js/form/element/input.js
@@ -5,10 +5,10 @@
define([
'underscore',
'Magento_Ui/js/form/element/abstract'
-], function (_, Acstract) {
+], function (_, Abstract) {
'use strict';
- return Acstract.extend({
+ return Abstract.extend({
defaults: {
prefixName: '',
prefixElementName: '',
diff --git a/app/code/Magento/Catalog/view/base/web/js/price-utils.js b/app/code/Magento/Catalog/view/base/web/js/price-utils.js
index e2ea42f7d5fe3..7b83d12cc9804 100644
--- a/app/code/Magento/Catalog/view/base/web/js/price-utils.js
+++ b/app/code/Magento/Catalog/view/base/web/js/price-utils.js
@@ -60,7 +60,7 @@ define([
pattern = pattern.indexOf('{sign}') < 0 ? s + pattern : pattern.replace('{sign}', s);
// we're avoiding the usage of to fixed, and using round instead with the e representation to address
- // numbers like 1.005 = 1.01. Using ToFixed to only provide trailig zeroes in case we have a whole number
+ // numbers like 1.005 = 1.01. Using ToFixed to only provide trailing zeroes in case we have a whole number
i = parseInt(
amount = Number(Math.round(Math.abs(+amount || 0) + 'e+' + precision) + ('e-' + precision)),
10
diff --git a/app/code/Magento/CatalogAnalytics/README.md b/app/code/Magento/CatalogAnalytics/README.md
index df125446117a3..f93b223c342d7 100644
--- a/app/code/Magento/CatalogAnalytics/README.md
+++ b/app/code/Magento/CatalogAnalytics/README.md
@@ -1,3 +1,3 @@
# Magento_CatalogAnalytics module
-The Magento_CatalogAnalytics module configures data definitions for a data collection related to the Catalog module entities to be used in [Advanced Reporting](http://devdocs.magento.com/guides/v2.2/advanced-reporting/modules.html).
+The Magento_CatalogAnalytics module configures data definitions for a data collection related to the Catalog module entities to be used in [Advanced Reporting](https://devdocs.magento.com/guides/v2.2/advanced-reporting/modules.html).
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
index ff53299b00e33..e910a5c8be4cd 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php
@@ -7,7 +7,7 @@
namespace Magento\CatalogGraphQl\Model\Resolver;
-use Magento\CatalogGraphQl\Model\Resolver\Layer\DataProvider\Filters;
+use Magento\Framework\Exception\InputException;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Filter;
use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Search;
@@ -17,6 +17,7 @@
use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\SearchFilter;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Catalog\Model\Layer\Resolver;
+use Magento\Framework\Api\Search\SearchCriteriaInterface;
/**
* Products field resolver, used for GraphQL request processing.
@@ -81,10 +82,10 @@ public function resolve(
} elseif (isset($args['search'])) {
$layerType = Resolver::CATALOG_LAYER_SEARCH;
$this->searchFilter->add($args['search'], $searchCriteria);
- $searchResult = $this->searchQuery->getResult($searchCriteria, $info);
+ $searchResult = $this->getSearchResult($this->searchQuery, $searchCriteria, $info);
} else {
$layerType = Resolver::CATALOG_LAYER_CATEGORY;
- $searchResult = $this->filterQuery->getResult($searchCriteria, $info);
+ $searchResult = $this->getSearchResult($this->filterQuery, $searchCriteria, $info);
}
//possible division by 0
if ($searchCriteria->getPageSize()) {
@@ -116,4 +117,25 @@ public function resolve(
return $data;
}
+
+ /**
+ * Get search result.
+ *
+ * @param Filter|Search $query
+ * @param SearchCriteriaInterface $searchCriteria
+ * @param ResolveInfo $info
+ *
+ * @return \Magento\CatalogGraphQl\Model\Resolver\Products\SearchResult
+ * @throws GraphQlInputException
+ */
+ private function getSearchResult($query, SearchCriteriaInterface $searchCriteria, ResolveInfo $info)
+ {
+ try {
+ $searchResult = $query->getResult($searchCriteria, $info);
+ } catch (InputException $e) {
+ throw new GraphQlInputException(__($e->getMessage()));
+ }
+
+ return $searchResult;
+ }
}
diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php
index 345d323e6e129..c047e9c9bef21 100644
--- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php
+++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php
@@ -1158,7 +1158,7 @@ protected function collectMultiselectValues($item, $attrCode, $storeId)
}
/**
- * Check attribute is valid
+ * Check attribute is valid.
*
* @param string $code
* @param mixed $value
@@ -1175,6 +1175,10 @@ protected function isValidAttributeValue($code, $value)
$isValid = false;
}
+ if (is_array($value)) {
+ $isValid = false;
+ }
+
return $isValid;
}
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php
index 7e6ada724a81a..3ac7f98818d70 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php
@@ -180,9 +180,9 @@ public function move($fileName, $renameFileOff = false)
}
$fileName = preg_replace('/[^a-z0-9\._-]+/i', '', $fileName);
- $filePath = $this->_directory->getRelativePath($filePath . $fileName);
+ $relativePath = $this->_directory->getRelativePath($filePath . $fileName);
$this->_directory->writeFile(
- $filePath,
+ $relativePath,
$read->readAll()
);
}
diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php
index 5e7210c1b7444..f9a49d4f8d121 100644
--- a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php
+++ b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php
@@ -9,11 +9,11 @@
use Magento\CatalogInventory\Api\StockConfigurationInterface;
use Magento\CatalogInventory\Model\ResourceModel\Stock\Item;
-use Magento\CatalogInventory\Model\Stock;
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceModifierInterface;
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructure;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\App\ObjectManager;
+use Magento\Framework\DB\Query\Generator;
/**
* Class for filter product price index.
@@ -40,22 +40,38 @@ class ProductPriceIndexFilter implements PriceModifierInterface
*/
private $connectionName;
+ /**
+ * @var Generator
+ */
+ private $batchQueryGenerator;
+
+ /**
+ * @var int
+ */
+ private $batchSize;
+
/**
* @param StockConfigurationInterface $stockConfiguration
* @param Item $stockItem
* @param ResourceConnection $resourceConnection
* @param string $connectionName
+ * @param Generator $batchQueryGenerator
+ * @param int $batchSize
*/
public function __construct(
StockConfigurationInterface $stockConfiguration,
Item $stockItem,
ResourceConnection $resourceConnection = null,
- $connectionName = 'indexer'
+ $connectionName = 'indexer',
+ Generator $batchQueryGenerator = null,
+ $batchSize = 100
) {
$this->stockConfiguration = $stockConfiguration;
$this->stockItem = $stockItem;
$this->resourceConnection = $resourceConnection ?: ObjectManager::getInstance()->get(ResourceConnection::class);
$this->connectionName = $connectionName;
+ $this->batchQueryGenerator = $batchQueryGenerator ?: ObjectManager::getInstance()->get(Generator::class);
+ $this->batchSize = $batchSize;
}
/**
@@ -76,32 +92,37 @@ public function modifyPrice(IndexTableStructure $priceTable, array $entityIds =
$connection = $this->resourceConnection->getConnection($this->connectionName);
$select = $connection->select();
+
$select->from(
- ['price_index' => $priceTable->getTableName()],
- []
- );
- $select->joinInner(
['stock_item' => $this->stockItem->getMainTable()],
- 'stock_item.product_id = price_index.' . $priceTable->getEntityField()
- . ' AND stock_item.stock_id = ' . Stock::DEFAULT_STOCK_ID,
- []
+ ['stock_item.product_id', 'MAX(stock_item.is_in_stock) as max_is_in_stock']
);
+
if ($this->stockConfiguration->getManageStock()) {
- $stockStatus = $connection->getCheckSql(
- 'use_config_manage_stock = 0 AND manage_stock = 0',
- Stock::STOCK_IN_STOCK,
- 'is_in_stock'
- );
+ $select->where('stock_item.use_config_manage_stock = 1 OR stock_item.manage_stock = 1');
} else {
- $stockStatus = $connection->getCheckSql(
- 'use_config_manage_stock = 0 AND manage_stock = 1',
- 'is_in_stock',
- Stock::STOCK_IN_STOCK
- );
+ $select->where('stock_item.use_config_manage_stock = 0 AND stock_item.manage_stock = 1');
}
- $select->where($stockStatus . ' = ?', Stock::STOCK_OUT_OF_STOCK);
- $query = $select->deleteFromSelect('price_index');
- $connection->query($query);
+ $select->group('stock_item.product_id');
+ $select->having('max_is_in_stock = 0');
+
+ $batchSelectIterator = $this->batchQueryGenerator->generate(
+ 'product_id',
+ $select,
+ $this->batchSize,
+ \Magento\Framework\DB\Query\BatchIteratorInterface::UNIQUE_FIELD_ITERATOR
+ );
+
+ foreach ($batchSelectIterator as $select) {
+ $productIds = null;
+ foreach ($connection->query($select)->fetchAll() as $row) {
+ $productIds[] = $row['product_id'];
+ }
+ if ($productIds !== null) {
+ $where = [$priceTable->getEntityField() .' IN (?)' => $productIds];
+ $connection->delete($priceTable->getTableName(), $where);
+ }
+ }
}
}
diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml
index 27e8d1e999907..706df79b1ef8b 100644
--- a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml
+++ b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml
@@ -104,6 +104,8 @@
+
+
diff --git a/app/code/Magento/CatalogRule/Model/Indexer/AbstractIndexer.php b/app/code/Magento/CatalogRule/Model/Indexer/AbstractIndexer.php
index 5d93e6f216866..6b7c12dfdf463 100644
--- a/app/code/Magento/CatalogRule/Model/Indexer/AbstractIndexer.php
+++ b/app/code/Magento/CatalogRule/Model/Indexer/AbstractIndexer.php
@@ -10,6 +10,9 @@
use Magento\Framework\DataObject\IdentityInterface;
use Magento\Framework\Indexer\CacheContext;
+/**
+ * Abstract class for CatalogRule indexers.
+ */
abstract class AbstractIndexer implements IndexerActionInterface, MviewActionInterface, IdentityInterface
{
/**
@@ -66,7 +69,6 @@ public function executeFull()
{
$this->indexBuilder->reindexFull();
$this->_eventManager->dispatch('clean_cache_by_tags', ['object' => $this]);
- //TODO: remove after fix fpc. MAGETWO-50668
$this->getCacheManager()->clean($this->getIdentities());
}
@@ -137,8 +139,9 @@ public function executeRow($id)
abstract protected function doExecuteRow($id);
/**
- * @return \Magento\Framework\App\CacheInterface|mixed
+ * Get cache manager
*
+ * @return \Magento\Framework\App\CacheInterface|mixed
* @deprecated 100.0.7
*/
private function getCacheManager()
diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml
index 2e09ee7134733..cac7c94de446f 100644
--- a/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml
+++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml
@@ -17,6 +17,9 @@
+
+
+
diff --git a/app/code/Magento/CatalogRule/etc/db_schema.xml b/app/code/Magento/CatalogRule/etc/db_schema.xml
index 6f7d713e49fe3..894f057ba73d1 100644
--- a/app/code/Magento/CatalogRule/etc/db_schema.xml
+++ b/app/code/Magento/CatalogRule/etc/db_schema.xml
@@ -23,7 +23,7 @@
-
@@ -49,7 +49,7 @@
default="0" comment="Product Id"/>
-
diff --git a/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php b/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php
index 17d70e38d3b61..184fd9cfd5b37 100644
--- a/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php
+++ b/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php
@@ -17,6 +17,11 @@
*/
class Result extends \Magento\Framework\App\Action\Action implements HttpGetActionInterface, HttpPostActionInterface
{
+ /**
+ * No results default handle.
+ */
+ const DEFAULT_NO_RESULT_HANDLE = 'catalogsearch_advanced_result_noresults';
+
/**
* Url factory
*
@@ -55,7 +60,16 @@ public function execute()
{
try {
$this->_catalogSearchAdvanced->addFilters($this->getRequest()->getQueryValue());
- $this->_view->loadLayout();
+ $size = $this->_catalogSearchAdvanced->getProductCollection()->getSize();
+
+ $handles = null;
+ if ($size == 0) {
+ $this->_view->getPage()->initLayout();
+ $handles = $this->_view->getLayout()->getUpdate()->getHandles();
+ $handles[] = static::DEFAULT_NO_RESULT_HANDLE;
+ }
+
+ $this->_view->loadLayout($handles);
$this->_view->renderLayout();
} catch (\Magento\Framework\Exception\LocalizedException $e) {
$this->messageManager->addError($e->getMessage());
diff --git a/app/code/Magento/CatalogSearch/Controller/Result/Index.php b/app/code/Magento/CatalogSearch/Controller/Result/Index.php
index fba4572cf2c10..975c6ba1e7eb9 100644
--- a/app/code/Magento/CatalogSearch/Controller/Result/Index.php
+++ b/app/code/Magento/CatalogSearch/Controller/Result/Index.php
@@ -20,6 +20,11 @@
*/
class Index extends \Magento\Framework\App\Action\Action implements HttpGetActionInterface, HttpPostActionInterface
{
+ /**
+ * No results default handle.
+ */
+ const DEFAULT_NO_RESULT_HANDLE = 'catalogsearch_result_index_noresults';
+
/**
* Catalog session
*
@@ -90,12 +95,19 @@ public function execute()
$getAdditionalRequestParameters = $this->getRequest()->getParams();
unset($getAdditionalRequestParameters[QueryFactory::QUERY_VAR_NAME]);
+ $handles = null;
+ if ($query->getNumResults() == 0) {
+ $this->_view->getPage()->initLayout();
+ $handles = $this->_view->getLayout()->getUpdate()->getHandles();
+ $handles[] = static::DEFAULT_NO_RESULT_HANDLE;
+ }
+
if (empty($getAdditionalRequestParameters) &&
$this->_objectManager->get(PopularSearchTerms::class)->isCacheable($queryText, $storeId)
) {
- $this->getCacheableResult($catalogSearchHelper, $query);
+ $this->getCacheableResult($catalogSearchHelper, $query, $handles);
} else {
- $this->getNotCacheableResult($catalogSearchHelper, $query);
+ $this->getNotCacheableResult($catalogSearchHelper, $query, $handles);
}
} else {
$this->getResponse()->setRedirect($this->_redirect->getRedirectUrl());
@@ -107,9 +119,10 @@ public function execute()
*
* @param \Magento\CatalogSearch\Helper\Data $catalogSearchHelper
* @param \Magento\Search\Model\Query $query
+ * @param array $handles
* @return void
*/
- private function getCacheableResult($catalogSearchHelper, $query)
+ private function getCacheableResult($catalogSearchHelper, $query, $handles)
{
if (!$catalogSearchHelper->isMinQueryLength()) {
$redirect = $query->getRedirect();
@@ -121,7 +134,7 @@ private function getCacheableResult($catalogSearchHelper, $query)
$catalogSearchHelper->checkNotes();
- $this->_view->loadLayout();
+ $this->_view->loadLayout($handles);
$this->_view->renderLayout();
}
@@ -130,11 +143,12 @@ private function getCacheableResult($catalogSearchHelper, $query)
*
* @param \Magento\CatalogSearch\Helper\Data $catalogSearchHelper
* @param \Magento\Search\Model\Query $query
+ * @param array $handles
* @return void
*
* @throws \Magento\Framework\Exception\LocalizedException
*/
- private function getNotCacheableResult($catalogSearchHelper, $query)
+ private function getNotCacheableResult($catalogSearchHelper, $query, $handles)
{
if ($catalogSearchHelper->isMinQueryLength()) {
$query->setId(0)->setIsActive(1)->setIsProcessed(1);
@@ -149,7 +163,7 @@ private function getNotCacheableResult($catalogSearchHelper, $query)
$catalogSearchHelper->checkNotes();
- $this->_view->loadLayout();
+ $this->_view->loadLayout($handles);
$this->getResponse()->setNoCacheHeaders();
$this->_view->renderLayout();
}
diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php
index 7aac6e98fc044..7b239d84bf962 100644
--- a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php
+++ b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php
@@ -91,12 +91,10 @@ protected function _getItemsData()
return $this->itemDataBuilder->build();
}
- $productSize = $productCollection->getSize();
-
$options = $attribute->getFrontend()
->getSelectOptions();
foreach ($options as $option) {
- $this->buildOptionData($option, $isAttributeFilterable, $optionsFacetedData, $productSize);
+ $this->buildOptionData($option, $isAttributeFilterable, $optionsFacetedData);
}
return $this->itemDataBuilder->build();
@@ -108,17 +106,16 @@ protected function _getItemsData()
* @param array $option
* @param boolean $isAttributeFilterable
* @param array $optionsFacetedData
- * @param int $productSize
* @return void
*/
- private function buildOptionData($option, $isAttributeFilterable, $optionsFacetedData, $productSize)
+ private function buildOptionData($option, $isAttributeFilterable, $optionsFacetedData)
{
$value = $this->getOptionValue($option);
if ($value === false) {
return;
}
$count = $this->getOptionCount($value, $optionsFacetedData);
- if ($isAttributeFilterable && (!$this->isOptionReducesResults($count, $productSize) || $count === 0)) {
+ if ($isAttributeFilterable && $count === 0) {
return;
}
diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Engine.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Engine.php
index 49caede8c4ac2..93ae2c94e2105 100644
--- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Engine.php
+++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Engine.php
@@ -110,7 +110,9 @@ public function processAttributeValue($attribute, $value)
&& in_array($attribute->getFrontendInput(), ['text', 'textarea'])
) {
$result = $value;
- } elseif ($this->isTermFilterableAttribute($attribute)) {
+ } elseif ($this->isTermFilterableAttribute($attribute)
+ || ($attribute->getIsSearchable() && in_array($attribute->getFrontendInput(), ['select', 'multiselect']))
+ ) {
$result = '';
}
@@ -119,6 +121,7 @@ public function processAttributeValue($attribute, $value)
/**
* Prepare index array as a string glued by separator
+ *
* Support 2 level array gluing
*
* @param array $index
diff --git a/app/code/Magento/CatalogSearch/Model/Search/QueryChecker/FullTextSearchCheck.php b/app/code/Magento/CatalogSearch/Model/Search/QueryChecker/FullTextSearchCheck.php
index c122bae15cb0c..a47ea54375205 100644
--- a/app/code/Magento/CatalogSearch/Model/Search/QueryChecker/FullTextSearchCheck.php
+++ b/app/code/Magento/CatalogSearch/Model/Search/QueryChecker/FullTextSearchCheck.php
@@ -25,7 +25,7 @@ class FullTextSearchCheck
* to join catalog_eav_attribute table to search query or not
*
* In case when the $query does not requires full text search
- * - we can skipp joining catalog_eav_attribute table because it becomes excessive
+ * - we can skip joining catalog_eav_attribute table because it becomes excessive
*
* @param QueryInterface $query
* @return bool
@@ -37,6 +37,8 @@ public function isRequiredForQuery(QueryInterface $query)
}
/**
+ * Process query
+ *
* @param QueryInterface $query
* @return bool
* @throws \InvalidArgumentException
@@ -62,6 +64,8 @@ private function processQuery(QueryInterface $query)
}
/**
+ * Process boolean query
+ *
* @param BoolExpression $query
* @return bool
* @throws \InvalidArgumentException
@@ -90,6 +94,8 @@ private function processBoolQuery(BoolExpression $query)
}
/**
+ * Process filter query
+ *
* @param Filter $query
* @return bool
* @throws \InvalidArgumentException
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php
index 891f008979e17..2faacea24262c 100644
--- a/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php
@@ -10,6 +10,11 @@
*/
class ResultTest extends \PHPUnit\Framework\TestCase
{
+ /**
+ * Test result action filters set before load layout scenario
+ *
+ * @return void
+ */
public function testResultActionFiltersSetBeforeLoadLayout()
{
$filters = null;
@@ -27,9 +32,15 @@ function () use (&$filters, $expectedQuery) {
$request = $this->createPartialMock(\Magento\Framework\App\Console\Request::class, ['getQueryValue']);
$request->expects($this->once())->method('getQueryValue')->will($this->returnValue($expectedQuery));
+ $collection = $this->createPartialMock(
+ \Magento\CatalogSearch\Model\ResourceModel\Advanced\Collection::class,
+ ['getSize']
+ );
+ $collection->expects($this->once())->method('getSize')->will($this->returnValue(1));
+
$catalogSearchAdvanced = $this->createPartialMock(
\Magento\CatalogSearch\Model\Advanced::class,
- ['addFilters', '__wakeup']
+ ['addFilters', '__wakeup', 'getProductCollection']
);
$catalogSearchAdvanced->expects($this->once())->method('addFilters')->will(
$this->returnCallback(
@@ -38,6 +49,8 @@ function ($added) use (&$filters) {
}
)
);
+ $catalogSearchAdvanced->expects($this->once())->method('getProductCollection')
+ ->will($this->returnValue($collection));
$objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
$context = $objectManager->getObject(
@@ -53,6 +66,11 @@ function ($added) use (&$filters) {
$instance->execute();
}
+ /**
+ * Test url set on exception scenario
+ *
+ * @return void
+ */
public function testUrlSetOnException()
{
$redirectResultMock = $this->createMock(\Magento\Framework\Controller\Result\Redirect::class);
@@ -131,11 +149,71 @@ public function testUrlSetOnException()
/** @var \Magento\CatalogSearch\Controller\Advanced\Result $instance */
$instance = $objectManager->getObject(
\Magento\CatalogSearch\Controller\Advanced\Result::class,
- ['context' => $contextMock,
- 'catalogSearchAdvanced' => $catalogSearchAdvanced,
- 'urlFactory' => $urlFactoryMock
+ [
+ 'context' => $contextMock,
+ 'catalogSearchAdvanced' => $catalogSearchAdvanced,
+ 'urlFactory' => $urlFactoryMock
]
);
$this->assertEquals($redirectResultMock, $instance->execute());
}
+
+ /**
+ * Test no result handle scenario
+ *
+ * @return void
+ */
+ public function testNoResultsHandle()
+ {
+ $expectedQuery = 'notExistTerm';
+
+ $update = $this->createPartialMock(\Magento\Framework\View\Model\Layout\Merge::class, ['getHandles']);
+ $update->expects($this->once())->method('getHandles')->will($this->returnValue([]));
+
+ $layout = $this->createPartialMock(\Magento\Framework\View\Result\Layout::class, ['getUpdate']);
+ $layout->expects($this->once())->method('getUpdate')->will($this->returnValue($update));
+
+ $page = $this->createPartialMock(\Magento\Framework\View\Result\Page::class, ['initLayout']);
+
+ $view = $this->createPartialMock(
+ \Magento\Framework\App\View::class,
+ ['loadLayout', 'renderLayout', 'getPage', 'getLayout']
+ );
+
+ $view->expects($this->once())->method('loadLayout')
+ ->with([\Magento\CatalogSearch\Controller\Advanced\Result::DEFAULT_NO_RESULT_HANDLE]);
+
+ $view->expects($this->once())->method('getPage')->will($this->returnValue($page));
+ $view->expects($this->once())->method('getLayout')->will($this->returnValue($layout));
+
+ $request = $this->createPartialMock(\Magento\Framework\App\Console\Request::class, ['getQueryValue']);
+ $request->expects($this->once())->method('getQueryValue')->will($this->returnValue($expectedQuery));
+
+ $collection = $this->createPartialMock(
+ \Magento\CatalogSearch\Model\ResourceModel\Advanced\Collection::class,
+ ['getSize']
+ );
+ $collection->expects($this->once())->method('getSize')->will($this->returnValue(0));
+
+ $catalogSearchAdvanced = $this->createPartialMock(
+ \Magento\CatalogSearch\Model\Advanced::class,
+ ['addFilters', '__wakeup', 'getProductCollection']
+ );
+
+ $catalogSearchAdvanced->expects($this->once())->method('getProductCollection')
+ ->will($this->returnValue($collection));
+
+ $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+ $context = $objectManager->getObject(
+ \Magento\Framework\App\Action\Context::class,
+ ['view' => $view, 'request' => $request]
+ );
+
+ /** @var \Magento\CatalogSearch\Controller\Advanced\Result $instance */
+ $instance = $objectManager->getObject(
+ \Magento\CatalogSearch\Controller\Advanced\Result::class,
+ ['context' => $context, 'catalogSearchAdvanced' => $catalogSearchAdvanced]
+ );
+ $instance->execute();
+ }
}
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/AttributeTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/AttributeTest.php
index abc0fdd1069fe..69e2c33d02d1a 100644
--- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/AttributeTest.php
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/AttributeTest.php
@@ -321,10 +321,6 @@ public function testGetItemsWithoutApply()
->method('build')
->will($this->returnValue($builtData));
- $this->fulltextCollection->expects($this->once())
- ->method('getSize')
- ->will($this->returnValue(50));
-
$expectedFilterItems = [
$this->createFilterItem(0, $builtData[0]['label'], $builtData[0]['value'], $builtData[0]['count']),
$this->createFilterItem(1, $builtData[1]['label'], $builtData[1]['value'], $builtData[1]['count']),
@@ -383,9 +379,6 @@ public function testGetItemsOnlyWithResults()
$this->fulltextCollection->expects($this->once())
->method('getFacetedData')
->willReturn($facetedData);
- $this->fulltextCollection->expects($this->once())
- ->method('getSize')
- ->will($this->returnValue(50));
$this->itemDataBuilder->expects($this->once())
->method('addItemData')
diff --git a/app/code/Magento/CatalogUrlRewrite/Model/ObjectRegistry.php b/app/code/Magento/CatalogUrlRewrite/Model/ObjectRegistry.php
index 019959f9c1fea..a048c216139e3 100644
--- a/app/code/Magento/CatalogUrlRewrite/Model/ObjectRegistry.php
+++ b/app/code/Magento/CatalogUrlRewrite/Model/ObjectRegistry.php
@@ -5,6 +5,9 @@
*/
namespace Magento\CatalogUrlRewrite\Model;
+/**
+ * Class ObjectRegistry
+ */
class ObjectRegistry
{
/**
@@ -26,15 +29,19 @@ public function __construct($entities)
}
/**
+ * Get Entity
+ *
* @param int $entityId
* @return \Magento\Framework\DataObject|null
*/
public function get($entityId)
{
- return isset($this->entitiesMap[$entityId]) ? $this->entitiesMap[$entityId] : null;
+ return $this->entitiesMap[$entityId] ?? null;
}
/**
+ * List Entities
+ *
* @return \Magento\Framework\DataObject[]
*/
public function getList()
diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserver.php
index 5d7e323e8b2d8..3cfd49b1d210a 100644
--- a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserver.php
+++ b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserver.php
@@ -100,16 +100,19 @@ public function execute(\Magento\Framework\Event\Observer $observer)
}
$mapsGenerated = false;
- if ($category->dataHasChangedFor('url_key')
- || $category->dataHasChangedFor('is_anchor')
- || !empty($category->getChangedProductIds())
- ) {
+ if ($this->isCategoryHasChanged($category)) {
if ($category->dataHasChangedFor('url_key')) {
$categoryUrlRewriteResult = $this->categoryUrlRewriteGenerator->generate($category);
$this->urlRewriteBunchReplacer->doBunchReplace($categoryUrlRewriteResult);
}
- $productUrlRewriteResult = $this->urlRewriteHandler->generateProductUrlRewrites($category);
- $this->urlRewriteBunchReplacer->doBunchReplace($productUrlRewriteResult);
+ if ($this->isChangedOnlyProduct($category)) {
+ $productUrlRewriteResult =
+ $this->urlRewriteHandler->updateProductUrlRewritesForChangedProduct($category);
+ $this->urlRewriteBunchReplacer->doBunchReplace($productUrlRewriteResult);
+ } else {
+ $productUrlRewriteResult = $this->urlRewriteHandler->generateProductUrlRewrites($category);
+ $this->urlRewriteBunchReplacer->doBunchReplace($productUrlRewriteResult);
+ }
$mapsGenerated = true;
}
@@ -119,6 +122,38 @@ public function execute(\Magento\Framework\Event\Observer $observer)
}
}
+ /**
+ * Check is category changed changed.
+ *
+ * @param Category $category
+ * @return bool
+ */
+ private function isCategoryHasChanged(Category $category): bool
+ {
+ if ($category->dataHasChangedFor('url_key')
+ || $category->dataHasChangedFor('is_anchor')
+ || !empty($category->getChangedProductIds())) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check is only product changed.
+ *
+ * @param Category $category
+ * @return bool
+ */
+ private function isChangedOnlyProduct(Category $category): bool
+ {
+ if (!empty($category->getChangedProductIds())
+ && !$category->dataHasChangedFor('is_anchor')
+ && !$category->dataHasChangedFor('url_key')) {
+ return true;
+ }
+ return false;
+ }
+
/**
* In case store_id is not set for category then we can assume that it was passed through product import.
* Store group must have only one root category, so receiving category's path and checking if one of it parts
diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php b/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php
index c4ec0bb3a74b2..b4a35f323e1bc 100644
--- a/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php
+++ b/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php
@@ -24,6 +24,8 @@
use Magento\UrlRewrite\Service\V1\Data\UrlRewrite;
/**
+ * Class for management url rewrites.
+ *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class UrlRewriteHandler
@@ -156,6 +158,30 @@ public function generateProductUrlRewrites(Category $category): array
}
/**
+ * Update product url rewrites for changed product.
+ *
+ * @param Category $category
+ * @return array
+ */
+ public function updateProductUrlRewritesForChangedProduct(Category $category): array
+ {
+ $mergeDataProvider = clone $this->mergeDataProviderPrototype;
+ $this->isSkippedProduct[$category->getEntityId()] = [];
+ $saveRewriteHistory = (bool)$category->getData('save_rewrites_history');
+ $storeIds = $this->getCategoryStoreIds($category);
+
+ if ($category->getChangedProductIds()) {
+ foreach ($storeIds as $storeId) {
+ $this->generateChangedProductUrls($mergeDataProvider, $category, (int)$storeId, $saveRewriteHistory);
+ }
+ }
+
+ return $mergeDataProvider->getData();
+ }
+
+ /**
+ * Delete category rewrites for children.
+ *
* @param Category $category
* @return void
*/
@@ -184,6 +210,8 @@ public function deleteCategoryRewritesForChildren(Category $category)
}
/**
+ * Get category products url rewrites.
+ *
* @param Category $category
* @param int $storeId
* @param bool $saveRewriteHistory
diff --git a/app/code/Magento/Checkout/Block/Onepage.php b/app/code/Magento/Checkout/Block/Onepage.php
index ca6b045ddbb5d..e01d5835b4cf0 100644
--- a/app/code/Magento/Checkout/Block/Onepage.php
+++ b/app/code/Magento/Checkout/Block/Onepage.php
@@ -38,7 +38,7 @@ class Onepage extends \Magento\Framework\View\Element\Template
protected $layoutProcessors;
/**
- * @var \Magento\Framework\Serialize\Serializer\Json
+ * @var \Magento\Framework\Serialize\SerializerInterface
*/
private $serializer;
@@ -48,8 +48,9 @@ class Onepage extends \Magento\Framework\View\Element\Template
* @param \Magento\Checkout\Model\CompositeConfigProvider $configProvider
* @param array $layoutProcessors
* @param array $data
- * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer
- * @throws \RuntimeException
+ * @param \Magento\Framework\Serialize\Serializer\Json $serializer
+ * @param \Magento\Framework\Serialize\SerializerInterface $serializerInterface
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function __construct(
\Magento\Framework\View\Element\Template\Context $context,
@@ -57,7 +58,8 @@ public function __construct(
\Magento\Checkout\Model\CompositeConfigProvider $configProvider,
array $layoutProcessors = [],
array $data = [],
- \Magento\Framework\Serialize\Serializer\Json $serializer = null
+ \Magento\Framework\Serialize\Serializer\Json $serializer = null,
+ \Magento\Framework\Serialize\SerializerInterface $serializerInterface = null
) {
parent::__construct($context, $data);
$this->formKey = $formKey;
@@ -65,12 +67,12 @@ public function __construct(
$this->jsLayout = isset($data['jsLayout']) && is_array($data['jsLayout']) ? $data['jsLayout'] : [];
$this->configProvider = $configProvider;
$this->layoutProcessors = $layoutProcessors;
- $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()
- ->get(\Magento\Framework\Serialize\Serializer\Json::class);
+ $this->serializer = $serializerInterface ?: \Magento\Framework\App\ObjectManager::getInstance()
+ ->get(\Magento\Framework\Serialize\Serializer\JsonHexTag::class);
}
/**
- * @return string
+ * @inheritdoc
*/
public function getJsLayout()
{
@@ -78,7 +80,7 @@ public function getJsLayout()
$this->jsLayout = $processor->process($this->jsLayout);
}
- return json_encode($this->jsLayout, JSON_HEX_TAG);
+ return $this->serializer->serialize($this->jsLayout);
}
/**
@@ -115,11 +117,13 @@ public function getBaseUrl()
}
/**
+ * Retrieve serialized checkout config.
+ *
* @return bool|string
* @since 100.2.0
*/
public function getSerializedCheckoutConfig()
{
- return json_encode($this->getCheckoutConfig(), JSON_HEX_TAG);
+ return $this->serializer->serialize($this->getCheckoutConfig());
}
}
diff --git a/app/code/Magento/Checkout/CustomerData/Cart.php b/app/code/Magento/Checkout/CustomerData/Cart.php
index ddb077462ef10..01e91d75c02d9 100644
--- a/app/code/Magento/Checkout/CustomerData/Cart.php
+++ b/app/code/Magento/Checkout/CustomerData/Cart.php
@@ -82,7 +82,7 @@ public function __construct(
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getSectionData()
{
@@ -158,11 +158,10 @@ protected function getRecentItems()
: $item->getProduct();
$products = $this->catalogUrl->getRewriteByProductStore([$product->getId() => $item->getStoreId()]);
- if (!isset($products[$product->getId()])) {
- continue;
+ if (isset($products[$product->getId()])) {
+ $urlDataObject = new \Magento\Framework\DataObject($products[$product->getId()]);
+ $item->getProduct()->setUrlDataObject($urlDataObject);
}
- $urlDataObject = new \Magento\Framework\DataObject($products[$product->getId()]);
- $item->getProduct()->setUrlDataObject($urlDataObject);
}
$items[] = $this->itemPoolInterface->getItemData($item);
}
diff --git a/app/code/Magento/Checkout/Model/DefaultConfigProvider.php b/app/code/Magento/Checkout/Model/DefaultConfigProvider.php
index fd120f0a37a4b..f30bd73deeae2 100644
--- a/app/code/Magento/Checkout/Model/DefaultConfigProvider.php
+++ b/app/code/Magento/Checkout/Model/DefaultConfigProvider.php
@@ -282,10 +282,12 @@ public function getConfig()
$quote = $this->checkoutSession->getQuote();
$quoteId = $quote->getId();
$email = $quote->getShippingAddress()->getEmail();
+ $quoteItemData = $this->getQuoteItemData();
$output['formKey'] = $this->formKey->getFormKey();
$output['customerData'] = $this->getCustomerData();
$output['quoteData'] = $this->getQuoteData();
- $output['quoteItemData'] = $this->getQuoteItemData();
+ $output['quoteItemData'] = $quoteItemData;
+ $output['quoteMessages'] = $this->getQuoteItemsMessages($quoteItemData);
$output['isCustomerLoggedIn'] = $this->isCustomerLoggedIn();
$output['selectedShippingMethod'] = $this->getSelectedShippingMethod();
if ($email && !$this->isCustomerLoggedIn()) {
@@ -316,6 +318,7 @@ public function getConfig()
);
$output['postCodes'] = $this->postCodesConfig->getPostCodes();
$output['imageData'] = $this->imageProvider->getImages($quoteId);
+
$output['totalsData'] = $this->getTotalsData();
$output['shippingPolicy'] = [
'isEnabled' => $this->scopeConfig->isSetFlag(
@@ -450,6 +453,7 @@ private function getQuoteItemData()
$quoteItem->getProduct(),
'product_thumbnail_image'
)->getUrl();
+ $quoteItemData[$index]['message'] = $quoteItem->getMessage();
}
}
return $quoteItemData;
@@ -776,4 +780,22 @@ private function getAttributeLabels(array $customAttribute, string $customAttrib
return $attributeOptionLabels;
}
+
+ /**
+ * Get notification messages for the quote items
+ *
+ * @param array $quoteItemData
+ * @return array
+ */
+ private function getQuoteItemsMessages(array $quoteItemData): array
+ {
+ $quoteItemsMessages = [];
+ if ($quoteItemData) {
+ foreach ($quoteItemData as $item) {
+ $quoteItemsMessages[$item['item_id']] = $item['message'];
+ }
+ }
+
+ return $quoteItemsMessages;
+ }
}
diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml
index 18bf70a613b50..464ccc1913335 100644
--- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml
@@ -21,11 +21,22 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -36,13 +47,18 @@
-
+
+
+
+
+
+
@@ -88,11 +104,18 @@
+
+
+
+
+
+
+
-
-
+
+
@@ -131,6 +154,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -161,7 +202,7 @@
-
+
@@ -236,6 +277,7 @@
+
@@ -243,4 +285,10 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingZipFormActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingZipFormActionGroup.xml
new file mode 100644
index 0000000000000..f12bf4344ab12
--- /dev/null
+++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingZipFormActionGroup.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml
index 62dd6b67b78a6..34f2cfe7f7fff 100644
--- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml
@@ -65,4 +65,11 @@
-
\ No newline at end of file
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Data/CountryData.xml b/app/code/Magento/Checkout/Test/Mftf/Data/CountryData.xml
index 26bc6ff641a9c..dc82932ec5ca7 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Data/CountryData.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Data/CountryData.xml
@@ -13,4 +13,26 @@
- Bahamas
+
+
+ - Australia
+ - Brazil
+ - Canada
+ - Croatia
+ - Estonia
+ - India
+ - Latvia
+ - Lithuania
+ - Romania
+ - Spain
+ - Switzerland
+ - United States
+ - Australia
+
+
+
+
+ - United Kingdom
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml
index ab82d9fdd93b5..0d3c6e419cc07 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml
@@ -31,5 +31,6 @@
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml
index 3dcf399c9ad54..0206c18b819c2 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml
@@ -23,7 +23,7 @@
-
+
@@ -53,5 +53,6 @@
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml
index 34819f641cbc9..bc65f8a2c0816 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml
@@ -16,6 +16,7 @@
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutPaymentMethodSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutPaymentMethodSection.xml
index c06af54f030e7..55c4385706ba9 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutPaymentMethodSection.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutPaymentMethodSection.xml
@@ -11,5 +11,6 @@
diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml
index 3f717943fe8f0..a894f2fbb1af9 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml
@@ -29,5 +29,6 @@
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml
index a41b1afc74368..e19627e7435d6 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml
@@ -55,8 +55,8 @@
-
-
+
+
@@ -81,7 +81,8 @@
-
+
+
@@ -105,7 +106,9 @@
-
+
+
+
@@ -165,6 +168,9 @@
+
+
+
@@ -198,6 +204,9 @@
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckNotVisibleProductInMinicartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckNotVisibleProductInMinicartTest.xml
new file mode 100644
index 0000000000000..4b4ca1935fd78
--- /dev/null
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/CheckNotVisibleProductInMinicartTest.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml
index 00d80cc2a94d9..35e0058440f6e 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml
@@ -158,13 +158,13 @@
-
+
-
+
-
+
-
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml
index 05a6939941f3e..371826c9e7841 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml
@@ -158,13 +158,13 @@
-
+
-
+
-
+
-
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml
new file mode 100644
index 0000000000000..aa90b7c1b54db
--- /dev/null
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml
index 1ef7403e94ce1..e7c2ad3dd28a4 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml
@@ -15,7 +15,7 @@
-
+
@@ -26,10 +26,13 @@
-
+
+
+
+
+
-
@@ -48,6 +51,8 @@
+
+
@@ -76,6 +81,7 @@
+
@@ -163,7 +169,8 @@
-
+
+
@@ -186,7 +193,8 @@
-
+
+
@@ -242,8 +250,7 @@
-
-
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml
new file mode 100644
index 0000000000000..0cc0dcf38e312
--- /dev/null
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml
index cc5e723c72ea0..8537e10ce5a03 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml
@@ -79,8 +79,12 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml
index f9533fd946f35..7b81f12624864 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml
@@ -69,6 +69,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -109,9 +128,10 @@
-
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml
new file mode 100644
index 0000000000000..778c1637a802d
--- /dev/null
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml
new file mode 100644
index 0000000000000..70f950f6f6c35
--- /dev/null
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{US_Address_CA.postcode}}
+ grabTextPostCode
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml
index ace50aa3e6d33..2cc21df85ab67 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml
@@ -36,6 +36,7 @@
+
diff --git a/app/code/Magento/Checkout/Test/Unit/Block/OnepageTest.php b/app/code/Magento/Checkout/Test/Unit/Block/OnepageTest.php
index 54f77c95148ac..b54339aa2c1d8 100644
--- a/app/code/Magento/Checkout/Test/Unit/Block/OnepageTest.php
+++ b/app/code/Magento/Checkout/Test/Unit/Block/OnepageTest.php
@@ -35,7 +35,7 @@ class OnepageTest extends \PHPUnit\Framework\TestCase
/**
* @var \PHPUnit_Framework_MockObject_MockObject
*/
- private $serializer;
+ private $serializerMock;
protected function setUp()
{
@@ -49,7 +49,7 @@ protected function setUp()
\Magento\Checkout\Block\Checkout\LayoutProcessorInterface::class
);
- $this->serializer = $this->createMock(\Magento\Framework\Serialize\Serializer\Json::class);
+ $this->serializerMock = $this->createMock(\Magento\Framework\Serialize\Serializer\JsonHexTag::class);
$this->model = new \Magento\Checkout\Block\Onepage(
$contextMock,
@@ -57,7 +57,8 @@ protected function setUp()
$this->configProviderMock,
[$this->layoutProcessorMock],
[],
- $this->serializer
+ $this->serializerMock,
+ $this->serializerMock
);
}
@@ -93,6 +94,7 @@ public function testGetJsLayout()
$processedLayout = ['layout' => ['processed' => true]];
$jsonLayout = '{"layout":{"processed":true}}';
$this->layoutProcessorMock->expects($this->once())->method('process')->with([])->willReturn($processedLayout);
+ $this->serializerMock->expects($this->once())->method('serialize')->willReturn($jsonLayout);
$this->assertEquals($jsonLayout, $this->model->getJsLayout());
}
@@ -101,6 +103,7 @@ public function testGetSerializedCheckoutConfig()
{
$checkoutConfig = ['checkout', 'config'];
$this->configProviderMock->expects($this->once())->method('getConfig')->willReturn($checkoutConfig);
+ $this->serializerMock->expects($this->once())->method('serialize')->willReturn(json_encode($checkoutConfig));
$this->assertEquals(json_encode($checkoutConfig), $this->model->getSerializedCheckoutConfig());
}
diff --git a/app/code/Magento/Checkout/etc/frontend/di.xml b/app/code/Magento/Checkout/etc/frontend/di.xml
index d80f88786c87b..00bcd2a27005a 100644
--- a/app/code/Magento/Checkout/etc/frontend/di.xml
+++ b/app/code/Magento/Checkout/etc/frontend/di.xml
@@ -59,6 +59,7 @@
- Magento\Checkout\Block\Checkout\TotalsProcessor
- Magento\Checkout\Block\Checkout\DirectoryDataProcessor
+ Magento\Framework\Serialize\Serializer\JsonHexTag
diff --git a/app/code/Magento/Checkout/view/frontend/layout/checkout_index_index.xml b/app/code/Magento/Checkout/view/frontend/layout/checkout_index_index.xml
index d4fadedf5d7a0..64b70e80bd84f 100644
--- a/app/code/Magento/Checkout/view/frontend/layout/checkout_index_index.xml
+++ b/app/code/Magento/Checkout/view/frontend/layout/checkout_index_index.xml
@@ -404,6 +404,10 @@
- Magento_Checkout/js/view/summary/item/details/subtotal
- after_details
+ -
+
- Magento_Checkout/js/view/summary/item/details/message
+ - item_message
+
diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/minicart.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/minicart.phtml
index e6d0260cf2305..20be9cd010c64 100644
--- a/app/code/Magento/Checkout/view/frontend/templates/cart/minicart.phtml
+++ b/app/code/Magento/Checkout/view/frontend/templates/cart/minicart.phtml
@@ -41,6 +41,14 @@
= $block->getChildHtml('minicart.addons') ?>
+
+
diff --git a/app/code/Magento/Customer/view/frontend/templates/address/grid.phtml b/app/code/Magento/Customer/view/frontend/templates/address/grid.phtml
new file mode 100644
index 0000000000000..7af5b5af2e7a1
--- /dev/null
+++ b/app/code/Magento/Customer/view/frontend/templates/address/grid.phtml
@@ -0,0 +1,82 @@
+getData('customer_address');
+?>
+
+
+
= $block->escapeHtml(__('Additional Address Entries')) ?>
+
+ getAdditionalAddresses()): ?>
+
+
+
+ = /* @escapeNotVerified */ __('Additional addresses') ?>
+
+
+ = /* @escapeNotVerified */ __('First Name') ?>
+ = /* @escapeNotVerified */ __('Last Name') ?>
+ = /* @escapeNotVerified */ __('Street Address') ?>
+ = /* @escapeNotVerified */ __('City') ?>
+ = /* @escapeNotVerified */ __('Country') ?>
+ = /* @escapeNotVerified */ __('State') ?>
+ = /* @escapeNotVerified */ __('Zip/Postal Code') ?>
+ = /* @escapeNotVerified */ __('Phone') ?>
+
+
+
+
+
+
+ = $block->escapeHtml($address->getFirstname()) ?>
+ = $block->escapeHtml($address->getLastname()) ?>
+ = $block->escapeHtml($block->getStreetAddress($address)) ?>
+ = $block->escapeHtml($address->getCity()) ?>
+ = /* @escapeNotVerified */ $block->getCountryByCode($address->getCountryId()) ?>
+ = /* @escapeNotVerified */ $address->getRegion()->getRegion() ?>
+ = $block->escapeHtml($address->getPostcode()) ?>
+ = $block->escapeHtml($address->getTelephone()) ?>
+
+ = $block->escapeHtml(__('Edit')) ?>
+ = $block->escapeHtml(__('Delete')) ?>
+
+
+
+
+
+
+ getChildHtml('pager')): ?>
+
= $block->getChildHtml('pager') ?>
+
+
+
= $block->escapeHtml(__('You have no other address entries in your address book.')) ?>
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/view/frontend/web/js/action/login.js b/app/code/Magento/Customer/view/frontend/web/js/action/login.js
index 0015e2732e383..d75b8f70c5346 100644
--- a/app/code/Magento/Customer/view/frontend/web/js/action/login.js
+++ b/app/code/Magento/Customer/view/frontend/web/js/action/login.js
@@ -31,11 +31,11 @@ define([
if (response.errors) {
messageContainer.addErrorMessage(response);
callbacks.forEach(function (callback) {
- callback(loginData, response);
+ callback(loginData);
});
} else {
callbacks.forEach(function (callback) {
- callback(loginData, response);
+ callback(loginData);
});
customerData.invalidate(['customer']);
diff --git a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js
index 39e3f8d95ee3b..66d37cb84e9cb 100644
--- a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js
+++ b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js
@@ -216,6 +216,9 @@ define([
$.cookieStorage.set(privateContentVersion, privateContent);
}
$.localStorage.set(privateContentVersion, privateContent);
+ _.each(dataProvider.getFromStorage(storage.keys()), function (sectionData, sectionName) {
+ buffer.notify(sectionName, sectionData);
+ });
this.reload([], false);
isLoading = true;
} else if (expiredSectionNames.length > 0) {
diff --git a/app/code/Magento/Customer/view/frontend/web/js/view/authentication-popup.js b/app/code/Magento/Customer/view/frontend/web/js/view/authentication-popup.js
index 37bd3a19df638..c14a59af49706 100644
--- a/app/code/Magento/Customer/view/frontend/web/js/view/authentication-popup.js
+++ b/app/code/Magento/Customer/view/frontend/web/js/view/authentication-popup.js
@@ -84,6 +84,7 @@ define([
if (formElement.validation() &&
formElement.validation('isValid')
) {
+ this.isLoading(true);
loginAction(loginData);
}
diff --git a/app/code/Magento/CustomerAnalytics/README.md b/app/code/Magento/CustomerAnalytics/README.md
index 8c64ce97629da..9658a8e7d90ed 100644
--- a/app/code/Magento/CustomerAnalytics/README.md
+++ b/app/code/Magento/CustomerAnalytics/README.md
@@ -1,3 +1,3 @@
# Magento_CustomerAnalytics module
-The Magento_CustomerAnalytics module configures data definitions for a data collection related to the Customer module entities to be used in [Advanced Reporting](http://devdocs.magento.com/guides/v2.2/advanced-reporting/modules.html).
+The Magento_CustomerAnalytics module configures data definitions for a data collection related to the Customer module entities to be used in [Advanced Reporting](https://devdocs.magento.com/guides/v2.2/advanced-reporting/modules.html).
diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressCreateDataValidator.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressCreateDataValidator.php
new file mode 100644
index 0000000000000..65672bcd3503b
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressCreateDataValidator.php
@@ -0,0 +1,56 @@
+getAllowedAddressAttributes = $getAllowedAddressAttributes;
+ }
+
+ /**
+ * Validate customer address create data
+ *
+ * @param array $addressData
+ * @return void
+ * @throws GraphQlInputException
+ */
+ public function validate(array $addressData): void
+ {
+ $attributes = $this->getAllowedAddressAttributes->execute();
+ $errorInput = [];
+
+ foreach ($attributes as $attributeName => $attributeInfo) {
+ if ($attributeInfo->getIsRequired()
+ && (!isset($addressData[$attributeName]) || empty($addressData[$attributeName]))
+ ) {
+ $errorInput[] = $attributeName;
+ }
+ }
+
+ if ($errorInput) {
+ throw new GraphQlInputException(
+ __('Required parameters are missing: %1', [implode(', ', $errorInput)])
+ );
+ }
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressDataProvider.php
new file mode 100644
index 0000000000000..9640953032ac6
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressDataProvider.php
@@ -0,0 +1,127 @@
+serviceOutputProcessor = $serviceOutputProcessor;
+ $this->jsonSerializer = $jsonSerializer;
+ $this->customerResourceModel = $customerResourceModel;
+ $this->customerFactory = $customerFactory;
+ }
+
+ /**
+ * Curate shipping and billing default options
+ *
+ * @param array $address
+ * @param AddressInterface $addressObject
+ * @return array
+ */
+ private function curateAddressDefaultValues(array $address, AddressInterface $addressObject) : array
+ {
+ $customerModel = $this->customerFactory->create();
+ $this->customerResourceModel->load($customerModel, $addressObject->getCustomerId());
+ $address[CustomerInterface::DEFAULT_BILLING] =
+ ($customerModel->getDefaultBillingAddress()
+ && $addressObject->getId() == $customerModel->getDefaultBillingAddress()->getId());
+ $address[CustomerInterface::DEFAULT_SHIPPING] =
+ ($customerModel->getDefaultShippingAddress()
+ && $addressObject->getId() == $customerModel->getDefaultShippingAddress()->getId());
+ return $address;
+ }
+
+ /**
+ * Transform single customer address data from object to in array format
+ *
+ * @param AddressInterface $addressObject
+ * @return array
+ */
+ public function getAddressData(AddressInterface $addressObject): array
+ {
+ $address = $this->serviceOutputProcessor->process(
+ $addressObject,
+ AddressRepositoryInterface::class,
+ 'getById'
+ );
+ $address = $this->curateAddressDefaultValues($address, $addressObject);
+
+ if (isset($address[CustomAttributesDataInterface::EXTENSION_ATTRIBUTES_KEY])) {
+ $address = array_merge($address, $address[CustomAttributesDataInterface::EXTENSION_ATTRIBUTES_KEY]);
+ }
+ $customAttributes = [];
+ if (isset($address[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES])) {
+ foreach ($address[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES] as $attribute) {
+ $isArray = false;
+ if (is_array($attribute['value'])) {
+ $isArray = true;
+ foreach ($attribute['value'] as $attributeValue) {
+ if (is_array($attributeValue)) {
+ $customAttributes[$attribute['attribute_code']] = $this->jsonSerializer->serialize(
+ $attribute['value']
+ );
+ continue;
+ }
+ $customAttributes[$attribute['attribute_code']] = implode(',', $attribute['value']);
+ continue;
+ }
+ }
+ if ($isArray) {
+ continue;
+ }
+ $customAttributes[$attribute['attribute_code']] = $attribute['value'];
+ }
+ }
+ $address = array_merge($address, $customAttributes);
+
+ return $address;
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressUpdateDataValidator.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressUpdateDataValidator.php
new file mode 100644
index 0000000000000..13716b491fddf
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressUpdateDataValidator.php
@@ -0,0 +1,56 @@
+getAllowedAddressAttributes = $getAllowedAddressAttributes;
+ }
+
+ /**
+ * Validate customer address update data
+ *
+ * @param array $addressData
+ * @return void
+ * @throws GraphQlInputException
+ */
+ public function validate(array $addressData): void
+ {
+ $attributes = $this->getAllowedAddressAttributes->execute();
+ $errorInput = [];
+
+ foreach ($attributes as $attributeName => $attributeInfo) {
+ if ($attributeInfo->getIsRequired()
+ && (isset($addressData[$attributeName]) && empty($addressData[$attributeName]))
+ ) {
+ $errorInput[] = $attributeName;
+ }
+ }
+
+ if ($errorInput) {
+ throw new GraphQlInputException(
+ __('Required parameters are missing: %1', [implode(', ', $errorInput)])
+ );
+ }
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetAllowedAddressAttributes.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetAllowedAddressAttributes.php
new file mode 100644
index 0000000000000..87be760732384
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetAllowedAddressAttributes.php
@@ -0,0 +1,49 @@
+eavConfig = $eavConfig;
+ }
+
+ /**
+ * Get allowed address attributes
+ *
+ * @return AbstractAttribute[]
+ */
+ public function execute(): array
+ {
+ $attributes = $this->eavConfig->getEntityAttributes(
+ AddressMetadataManagementInterface::ENTITY_TYPE_ADDRESS
+ );
+ foreach ($attributes as $attributeCode => $attribute) {
+ if (false === $attribute->getIsVisibleOnFront()) {
+ unset($attributes[$attributeCode]);
+ }
+ }
+ return $attributes;
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetCustomerAddressForUser.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetCustomerAddressForUser.php
new file mode 100644
index 0000000000000..f7323402a6c62
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetCustomerAddressForUser.php
@@ -0,0 +1,61 @@
+addressRepository = $addressRepository;
+ }
+
+ /**
+ * Get customer address for user
+ *
+ * @param int $addressId
+ * @param int $userId
+ * @return AddressInterface
+ * @throws GraphQlAuthorizationException
+ * @throws GraphQlNoSuchEntityException
+ */
+ public function execute(int $addressId, int $userId): AddressInterface
+ {
+ try {
+ /** @var AddressInterface $address */
+ $address = $this->addressRepository->getById($addressId);
+ } catch (NoSuchEntityException $e) {
+ throw new GraphQlNoSuchEntityException(
+ __('Address id %1 does not exist.', [$addressId])
+ );
+ }
+
+ if ($address->getCustomerId() != $userId) {
+ throw new GraphQlAuthorizationException(
+ __('Current customer does not have permission to address id %1', [$addressId])
+ );
+ }
+ return $address;
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/CustomerDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Customer/CustomerDataProvider.php
index 646d426050b70..c8382593eab23 100644
--- a/app/code/Magento/CustomerGraphQl/Model/Customer/CustomerDataProvider.php
+++ b/app/code/Magento/CustomerGraphQl/Model/Customer/CustomerDataProvider.php
@@ -70,6 +70,25 @@ public function getCustomerById(int $customerId): array
return $this->processCustomer($customer);
}
+ /**
+ * Curate default shipping and default billing keys
+ *
+ * @param array $arrayAddress
+ * @return array
+ */
+ private function curateAddressData(array $arrayAddress) : array
+ {
+ foreach ($arrayAddress as $key => $address) {
+ if (!isset($address['default_shipping'])) {
+ $arrayAddress[$key]['default_shipping'] = false;
+ }
+ if (!isset($address['default_billing'])) {
+ $arrayAddress[$key]['default_billing'] = false;
+ }
+ }
+ return $arrayAddress;
+ }
+
/**
* Transform single customer data from object to in array format
*
@@ -83,6 +102,7 @@ private function processCustomer(CustomerInterface $customer): array
CustomerRepositoryInterface::class,
'get'
);
+ $customerData['addresses'] = $this->curateAddressData($customerData['addresses']);
if (isset($customerData['extension_attributes'])) {
$customerData = array_merge($customerData, $customerData['extension_attributes']);
}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateAccountInformation.php b/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerData.php
similarity index 97%
rename from app/code/Magento/CustomerGraphQl/Model/Customer/UpdateAccountInformation.php
rename to app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerData.php
index 36365f1910f27..18510b872e64a 100644
--- a/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateAccountInformation.php
+++ b/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerData.php
@@ -15,9 +15,9 @@
use Magento\Store\Model\StoreManagerInterface;
/**
- * Update account information
+ * Update customer data
*/
-class UpdateAccountInformation
+class UpdateCustomerData
{
/**
* @var CustomerRepositoryInterface
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomerAddress.php
new file mode 100644
index 0000000000000..823444e5a2d7d
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomerAddress.php
@@ -0,0 +1,124 @@
+checkCustomerAccount = $checkCustomerAccount;
+ $this->addressRepository = $addressRepository;
+ $this->addressInterfaceFactory = $addressInterfaceFactory;
+ $this->customerAddressDataProvider = $customerAddressDataProvider;
+ $this->dataObjectHelper = $dataObjectHelper;
+ $this->customerAddressCreateDataValidator = $customerAddressCreateDataValidator;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(
+ Field $field,
+ $context,
+ ResolveInfo $info,
+ array $value = null,
+ array $args = null
+ ) {
+ $currentUserId = $context->getUserId();
+ $currentUserType = $context->getUserType();
+
+ $this->checkCustomerAccount->execute($currentUserId, $currentUserType);
+ $this->customerAddressCreateDataValidator->validate($args['input']);
+
+ $address = $this->createCustomerAddress((int)$currentUserId, $args['input']);
+ return $this->customerAddressDataProvider->getAddressData($address);
+ }
+
+ /**
+ * Create customer address
+ *
+ * @param int $customerId
+ * @param array $addressData
+ * @return AddressInterface
+ * @throws GraphQlInputException
+ */
+ private function createCustomerAddress(int $customerId, array $addressData) : AddressInterface
+ {
+ /** @var AddressInterface $address */
+ $address = $this->addressInterfaceFactory->create();
+ $this->dataObjectHelper->populateWithArray($address, $addressData, AddressInterface::class);
+ $address->setCustomerId($customerId);
+
+ try {
+ $address = $this->addressRepository->save($address);
+ } catch (InputException $e) {
+ throw new GraphQlInputException(__($e->getMessage()), $e);
+ }
+ return $address;
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/DeleteCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/DeleteCustomerAddress.php
new file mode 100644
index 0000000000000..084857c84d5a4
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/DeleteCustomerAddress.php
@@ -0,0 +1,96 @@
+checkCustomerAccount = $checkCustomerAccount;
+ $this->addressRepository = $addressRepository;
+ $this->getCustomerAddressForUser = $getCustomerAddressForUser;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(
+ Field $field,
+ $context,
+ ResolveInfo $info,
+ array $value = null,
+ array $args = null
+ ) {
+ $currentUserId = $context->getUserId();
+ $currentUserType = $context->getUserType();
+
+ $this->checkCustomerAccount->execute($currentUserId, $currentUserType);
+
+ return $this->deleteCustomerAddress((int)$currentUserId, (int)$args['id']);
+ }
+
+ /**
+ * Delete customer address
+ *
+ * @param int $customerId
+ * @param int $addressId
+ * @return bool
+ * @throws GraphQlAuthorizationException
+ * @throws GraphQlNoSuchEntityException
+ */
+ private function deleteCustomerAddress($customerId, $addressId)
+ {
+ $address = $this->getCustomerAddressForUser->execute($addressId, $customerId);
+ if ($address->isDefaultBilling()) {
+ throw new GraphQlAuthorizationException(
+ __('Customer Address %1 is set as default billing address and can not be deleted', [$addressId])
+ );
+ }
+ if ($address->isDefaultShipping()) {
+ throw new GraphQlAuthorizationException(
+ __('Customer Address %1 is set as default shipping address and can not be deleted', [$addressId])
+ );
+ }
+ return $this->addressRepository->delete($address);
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php
index 5dc857f3f178c..50760d2e2e31c 100644
--- a/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php
@@ -9,7 +9,7 @@
use Magento\CustomerGraphQl\Model\Customer\ChangeSubscriptionStatus;
use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount;
-use Magento\CustomerGraphQl\Model\Customer\UpdateAccountInformation;
+use Magento\CustomerGraphQl\Model\Customer\UpdateCustomerData;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\CustomerGraphQl\Model\Customer\CustomerDataProvider;
@@ -27,9 +27,9 @@ class UpdateCustomer implements ResolverInterface
private $checkCustomerAccount;
/**
- * @var UpdateAccountInformation
+ * @var UpdateCustomerData
*/
- private $updateAccountInformation;
+ private $updateCustomerData;
/**
* @var ChangeSubscriptionStatus
@@ -43,18 +43,18 @@ class UpdateCustomer implements ResolverInterface
/**
* @param CheckCustomerAccount $checkCustomerAccount
- * @param UpdateAccountInformation $updateAccountInformation
+ * @param UpdateCustomerData $updateCustomerData
* @param ChangeSubscriptionStatus $changeSubscriptionStatus
* @param CustomerDataProvider $customerDataProvider
*/
public function __construct(
CheckCustomerAccount $checkCustomerAccount,
- UpdateAccountInformation $updateAccountInformation,
+ UpdateCustomerData $updateCustomerData,
ChangeSubscriptionStatus $changeSubscriptionStatus,
CustomerDataProvider $customerDataProvider
) {
$this->checkCustomerAccount = $checkCustomerAccount;
- $this->updateAccountInformation = $updateAccountInformation;
+ $this->updateCustomerData = $updateCustomerData;
$this->changeSubscriptionStatus = $changeSubscriptionStatus;
$this->customerDataProvider = $customerDataProvider;
}
@@ -79,7 +79,7 @@ public function resolve(
$this->checkCustomerAccount->execute($currentUserId, $currentUserType);
$currentUserId = (int)$currentUserId;
- $this->updateAccountInformation->execute($currentUserId, $args['input']);
+ $this->updateCustomerData->execute($currentUserId, $args['input']);
if (isset($args['input']['is_subscribed'])) {
$this->changeSubscriptionStatus->execute($currentUserId, (bool)$args['input']['is_subscribed']);
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomerAddress.php
new file mode 100644
index 0000000000000..7bae40e4cc5de
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomerAddress.php
@@ -0,0 +1,114 @@
+checkCustomerAccount = $checkCustomerAccount;
+ $this->addressRepository = $addressRepository;
+ $this->customerAddressDataProvider = $customerAddressDataProvider;
+ $this->dataObjectHelper = $dataObjectHelper;
+ $this->customerAddressUpdateDataValidator = $customerAddressUpdateDataValidator;
+ $this->getCustomerAddressForUser = $getCustomerAddressForUser;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(
+ Field $field,
+ $context,
+ ResolveInfo $info,
+ array $value = null,
+ array $args = null
+ ) {
+ $currentUserId = $context->getUserId();
+ $currentUserType = $context->getUserType();
+
+ $this->checkCustomerAccount->execute($currentUserId, $currentUserType);
+ $this->customerAddressUpdateDataValidator->validate($args['input']);
+
+ $address = $this->updateCustomerAddress((int)$currentUserId, (int)$args['id'], $args['input']);
+ return $this->customerAddressDataProvider->getAddressData($address);
+ }
+
+ /**
+ * Update customer address
+ *
+ * @param int $customerId
+ * @param int $addressId
+ * @param array $addressData
+ * @return AddressInterface
+ */
+ private function updateCustomerAddress(int $customerId, int $addressId, array $addressData)
+ {
+ $address = $this->getCustomerAddressForUser->execute($addressId, $customerId);
+ $this->dataObjectHelper->populateWithArray($address, $addressData, AddressInterface::class);
+ return $this->addressRepository->save($address);
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/composer.json b/app/code/Magento/CustomerGraphQl/composer.json
index 488013c1ee586..722aba3aa0658 100644
--- a/app/code/Magento/CustomerGraphQl/composer.json
+++ b/app/code/Magento/CustomerGraphQl/composer.json
@@ -6,6 +6,7 @@
"php": "~7.1.3||~7.2.0",
"magento/module-customer": "*",
"magento/module-authorization": "*",
+ "magento/module-eav": "*",
"magento/module-newsletter": "*",
"magento/module-integration": "*",
"magento/module-store": "*",
diff --git a/app/code/Magento/CustomerGraphQl/etc/module.xml b/app/code/Magento/CustomerGraphQl/etc/module.xml
index bde93c6276500..eeed4862bbbfd 100644
--- a/app/code/Magento/CustomerGraphQl/etc/module.xml
+++ b/app/code/Magento/CustomerGraphQl/etc/module.xml
@@ -6,11 +6,5 @@
*/
-->
-
-
-
-
-
-
-
+
diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls
index ca4ac6cc245a7..c92753b96225c 100644
--- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls
@@ -10,6 +10,40 @@ type Mutation {
changeCustomerPassword(currentPassword: String!, newPassword: String!): Customer @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\ChangePassword") @doc(description:"Changes the password for the logged-in customer")
updateCustomer (input: UpdateCustomerInput!): UpdateCustomerOutput @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\UpdateCustomer") @doc(description:"Update the customer's personal information")
revokeCustomerToken: RevokeCustomerTokenOutput @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\RevokeCustomerToken") @doc(description:"Revoke the customer token")
+ createCustomerAddress(input: CustomerAddressInput!): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\CreateCustomerAddress") @doc(description: "Create customer address")
+ updateCustomerAddress(id: Int!, input: CustomerAddressInput): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\UpdateCustomerAddress") @doc(description: "Update customer address")
+ deleteCustomerAddress(id: Int!): Boolean @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\DeleteCustomerAddress") @doc(description: "Delete customer address")
+}
+
+input CustomerAddressInput {
+ firstname: String @doc(description: "The first name of the person associated with the shipping/billing address")
+ lastname: String @doc(description: "The family name of the person associated with the shipping/billing address")
+ company: String @doc(description: "The customer's company")
+ telephone: String @doc(description: "The telephone number")
+ street: [String] @doc(description: "An array of strings that define the street number and name")
+ city: String @doc(description: "The city or town")
+ region: CustomerAddressRegionInput @doc(description: "An object containing the region name, region code, and region ID")
+ postcode: String @doc(description: "The customer's ZIP or postal code")
+ country_id: CountryCodeEnum @doc(description: "The customer's country")
+ default_shipping: Boolean @doc(description: "Indicates whether the address is the default shipping address")
+ default_billing: Boolean @doc(description: "Indicates whether the address is the default billing address")
+ fax: String @doc(description: "The fax number")
+ middlename: String @doc(description: "The middle name of the person associated with the shipping/billing address")
+ prefix: String @doc(description: "An honorific, such as Dr., Mr., or Mrs.")
+ suffix: String @doc(description: "A value such as Sr., Jr., or III")
+ vat_id: String @doc(description: "The customer's Tax/VAT number (for corporate customers)")
+ custom_attributes: [CustomerAddressAttributeInput] @doc(description: "Address custom attributes")
+}
+
+input CustomerAddressRegionInput @doc(description: "CustomerAddressRegionInput defines the customer's state or province") {
+ region_code: String @doc(description: "The address region code")
+ region: String @doc(description: "The state or province name")
+ region_id: Int @doc(description: "Uniquely identifies the region")
+}
+
+input CustomerAddressAttributeInput {
+ attribute_code: String! @doc(description: "Attribute code")
+ value: String! @doc(description: "Attribute value")
}
type CustomerToken {
@@ -70,6 +104,8 @@ type CustomerAddress @doc(description: "CustomerAddress contains detailed inform
vat_id: String @doc(description: "The customer's Tax/VAT number (for corporate customers)")
default_shipping: Boolean @doc(description: "Indicates whether the address is the default shipping address")
default_billing: Boolean @doc(description: "Indicates whether the address is the default billing address")
+ custom_attributes: [CustomerAddressAttribute] @doc(description: "Address custom attributes")
+ extension_attributes: [CustomerAddressAttribute] @doc(description: "Address extension attributes")
}
type CustomerAddressRegion @doc(description: "CustomerAddressRegion defines the customer's state or province") {
@@ -77,3 +113,256 @@ type CustomerAddressRegion @doc(description: "CustomerAddressRegion defines the
region: String @doc(description: "The state or province name")
region_id: Int @doc(description: "Uniquely identifies the region")
}
+
+type CustomerAddressAttribute {
+ attribute_code: String @doc(description: "Attribute code")
+ value: String @doc(description: "Attribute value")
+}
+
+enum CountryCodeEnum @doc(description: "The list of countries codes") {
+ AF @doc(description: "Afghanistan")
+ AX @doc(description: "Åland Islands")
+ AL @doc(description: "Albania")
+ DZ @doc(description: "Algeria")
+ AS @doc(description: "American Samoa")
+ AD @doc(description: "Andorra")
+ AO @doc(description: "Angola")
+ AI @doc(description: "Anguilla")
+ AQ @doc(description: "Antarctica")
+ AG @doc(description: "Antigua & Barbuda")
+ AR @doc(description: "Argentina")
+ AM @doc(description: "Armenia")
+ AW @doc(description: "Aruba")
+ AU @doc(description: "Australia")
+ AT @doc(description: "Austria")
+ AZ @doc(description: "Azerbaijan")
+ BS @doc(description: "Bahamas")
+ BH @doc(description: "Bahrain")
+ BD @doc(description: "Bangladesh")
+ BB @doc(description: "Barbados")
+ BY @doc(description: "Belarus")
+ BE @doc(description: "Belgium")
+ BZ @doc(description: "Belize")
+ BJ @doc(description: "Benin")
+ BM @doc(description: "Bermuda")
+ BT @doc(description: "Bhutan")
+ BO @doc(description: "Bolivia")
+ BA @doc(description: "Bosnia & Herzegovina")
+ BW @doc(description: "Botswana")
+ BV @doc(description: "Bouvet Island")
+ BR @doc(description: "Brazil")
+ IO @doc(description: "British Indian Ocean Territory")
+ VG @doc(description: "British Virgin Islands")
+ BN @doc(description: "Brunei")
+ BG @doc(description: "Bulgaria")
+ BF @doc(description: "Burkina Faso")
+ BI @doc(description: "Burundi")
+ KH @doc(description: "Cambodia")
+ CM @doc(description: "Cameroon")
+ CA @doc(description: "Canada")
+ CV @doc(description: "Cape Verde")
+ KY @doc(description: "Cayman Islands")
+ CF @doc(description: "Central African Republic")
+ TD @doc(description: "Chad")
+ CL @doc(description: "Chile")
+ CN @doc(description: "China")
+ CX @doc(description: "Christmas Island")
+ CC @doc(description: "Cocos (Keeling) Islands")
+ CO @doc(description: "Colombia")
+ KM @doc(description: "Comoros")
+ CG @doc(description: "Congo -Brazzaville")
+ CD @doc(description: "Congo - Kinshasa")
+ CK @doc(description: "Cook Islands")
+ CR @doc(description: "Costa Rica")
+ CI @doc(description: "Côte d’Ivoire")
+ HR @doc(description: "Croatia")
+ CU @doc(description: "Cuba")
+ CY @doc(description: "Cyprus")
+ CZ @doc(description: "Czech Republic")
+ DK @doc(description: "Denmark")
+ DJ @doc(description: "Djibouti")
+ DM @doc(description: "Dominica")
+ DO @doc(description: "Dominican Republic")
+ EC @doc(description: "Ecuador")
+ EG @doc(description: "Egypt")
+ SV @doc(description: "El Salvador")
+ GQ @doc(description: "Equatorial Guinea")
+ ER @doc(description: "Eritrea")
+ EE @doc(description: "Estonia")
+ ET @doc(description: "Ethiopia")
+ FK @doc(description: "Falkland Islands")
+ FO @doc(description: "Faroe Islands")
+ FJ @doc(description: "Fiji")
+ FI @doc(description: "Finland")
+ FR @doc(description: "France")
+ GF @doc(description: "French Guiana")
+ PF @doc(description: "French Polynesia")
+ TF @doc(description: "French Southern Territories")
+ GA @doc(description: "Gabon")
+ GM @doc(description: "Gambia")
+ GE @doc(description: "Georgia")
+ DE @doc(description: "Germany")
+ GH @doc(description: "Ghana")
+ GI @doc(description: "Gibraltar")
+ GR @doc(description: "Greece")
+ GL @doc(description: "Greenland")
+ GD @doc(description: "Grenada")
+ GP @doc(description: "Guadeloupe")
+ GU @doc(description: "Guam")
+ GT @doc(description: "Guatemala")
+ GG @doc(description: "Guernsey")
+ GN @doc(description: "Guinea")
+ GW @doc(description: "Guinea-Bissau")
+ GY @doc(description: "Guyana")
+ HT @doc(description: "Haiti")
+ HM @doc(description: "Heard & McDonald Islands")
+ HN @doc(description: "Honduras")
+ HK @doc(description: "Hong Kong SAR China")
+ HU @doc(description: "Hungary")
+ IS @doc(description: "Iceland")
+ IN @doc(description: "India")
+ ID @doc(description: "Indonesia")
+ IR @doc(description: "Iran")
+ IQ @doc(description: "Iraq")
+ IE @doc(description: "Ireland")
+ IM @doc(description: "Isle of Man")
+ IL @doc(description: "Israel")
+ IT @doc(description: "Italy")
+ JM @doc(description: "Jamaica")
+ JP @doc(description: "Japan")
+ JE @doc(description: "Jersey")
+ JO @doc(description: "Jordan")
+ KZ @doc(description: "Kazakhstan")
+ KE @doc(description: "Kenya")
+ KI @doc(description: "Kiribati")
+ KW @doc(description: "Kuwait")
+ KG @doc(description: "Kyrgyzstan")
+ LA @doc(description: "Laos")
+ LV @doc(description: "Latvia")
+ LB @doc(description: "Lebanon")
+ LS @doc(description: "Lesotho")
+ LR @doc(description: "Liberia")
+ LY @doc(description: "Libya")
+ LI @doc(description: "Liechtenstein")
+ LT @doc(description: "Lithuania")
+ LU @doc(description: "Luxembourg")
+ MO @doc(description: "Macau SAR China")
+ MK @doc(description: "Macedonia")
+ MG @doc(description: "Madagascar")
+ MW @doc(description: "Malawi")
+ MY @doc(description: "Malaysia")
+ MV @doc(description: "Maldives")
+ ML @doc(description: "Mali")
+ MT @doc(description: "Malta")
+ MH @doc(description: "Marshall Islands")
+ MQ @doc(description: "Martinique")
+ MR @doc(description: "Mauritania")
+ MU @doc(description: "Mauritius")
+ YT @doc(description: "Mayotte")
+ MX @doc(description: "Mexico")
+ FM @doc(description: "Micronesia")
+ MD @doc(description: "Moldova")
+ MC @doc(description: "Monaco")
+ MN @doc(description: "Mongolia")
+ ME @doc(description: "Montenegro")
+ MS @doc(description: "Montserrat")
+ MA @doc(description: "Morocco")
+ MZ @doc(description: "Mozambique")
+ MM @doc(description: "Myanmar (Burma)")
+ NA @doc(description: "Namibia")
+ NR @doc(description: "Nauru")
+ NP @doc(description: "Nepal")
+ NL @doc(description: "Netherlands")
+ AN @doc(description: "Netherlands Antilles")
+ NC @doc(description: "New Caledonia")
+ NZ @doc(description: "New Zealand")
+ NI @doc(description: "Nicaragua")
+ NE @doc(description: "Niger")
+ NG @doc(description: "Nigeria")
+ NU @doc(description: "Niue")
+ NF @doc(description: "Norfolk Island")
+ MP @doc(description: "Northern Mariana Islands")
+ KP @doc(description: "North Korea")
+ NO @doc(description: "Norway")
+ OM @doc(description: "Oman")
+ PK @doc(description: "Pakistan")
+ PW @doc(description: "Palau")
+ PS @doc(description: "Palestinian Territories")
+ PA @doc(description: "Panama")
+ PG @doc(description: "Papua New Guinea")
+ PY @doc(description: "Paraguay")
+ PE @doc(description: "Peru")
+ PH @doc(description: "Philippines")
+ PN @doc(description: "Pitcairn Islands")
+ PL @doc(description: "Poland")
+ PT @doc(description: "Portugal")
+ QA @doc(description: "Qatar")
+ RE @doc(description: "Réunion")
+ RO @doc(description: "Romania")
+ RU @doc(description: "Russia")
+ RW @doc(description: "Rwanda")
+ WS @doc(description: "Samoa")
+ SM @doc(description: "San Marino")
+ ST @doc(description: "São Tomé & Príncipe")
+ SA @doc(description: "Saudi Arabia")
+ SN @doc(description: "Senegal")
+ RS @doc(description: "Serbia")
+ SC @doc(description: "Seychelles")
+ SL @doc(description: "Sierra Leone")
+ SG @doc(description: "Singapore")
+ SK @doc(description: "Slovakia")
+ SI @doc(description: "Slovenia")
+ SB @doc(description: "Solomon Islands")
+ SO @doc(description: "Somalia")
+ ZA @doc(description: "South Africa")
+ GS @doc(description: "South Georgia & South Sandwich Islands")
+ KR @doc(description: "South Korea")
+ ES @doc(description: "Spain")
+ LK @doc(description: "Sri Lanka")
+ BL @doc(description: "St. Barthélemy")
+ SH @doc(description: "St. Helena")
+ KN @doc(description: "St. Kitts & Nevis")
+ LC @doc(description: "St. Lucia")
+ MF @doc(description: "St. Martin")
+ PM @doc(description: "St. Pierre & Miquelon")
+ VC @doc(description: "St. Vincent & Grenadines")
+ SD @doc(description: "Sudan")
+ SR @doc(description: "Suriname")
+ SJ @doc(description: "Svalbard & Jan Mayen")
+ SZ @doc(description: "Swaziland")
+ SE @doc(description: "Sweden")
+ CH @doc(description: "Switzerland")
+ SY @doc(description: "Syria")
+ TW @doc(description: "Taiwan")
+ TJ @doc(description: "Tajikistan")
+ TZ @doc(description: "Tanzania")
+ TH @doc(description: "Thailand")
+ TL @doc(description: "Timor-Leste")
+ TG @doc(description: "Togo")
+ TK @doc(description: "Tokelau")
+ TO @doc(description: "Tonga")
+ TT @doc(description: "Trinidad & Tobago")
+ TN @doc(description: "Tunisia")
+ TR @doc(description: "Turkey")
+ TM @doc(description: "Turkmenistan")
+ TC @doc(description: "Turks & Caicos Islands")
+ TV @doc(description: "Tuvalu")
+ UG @doc(description: "Uganda")
+ UA @doc(description: "Ukraine")
+ AE @doc(description: "United Arab Emirates")
+ GB @doc(description: "United Kingdom")
+ US @doc(description: "United States")
+ UY @doc(description: "Uruguay")
+ UM @doc(description: "U.S. Outlying Islands")
+ VI @doc(description: "U.S. Virgin Islands")
+ UZ @doc(description: "Uzbekistan")
+ VU @doc(description: "Vanuatu")
+ VA @doc(description: "Vatican City")
+ VE @doc(description: "Venezuela")
+ VN @doc(description: "Vietnam")
+ WF @doc(description: "Wallis & Futuna")
+ EH @doc(description: "Western Sahara")
+ YE @doc(description: "Yemen")
+ ZM @doc(description: "Zambia")
+ ZW @doc(description: "Zimbabwe")
+}
\ No newline at end of file
diff --git a/app/code/Magento/Deploy/Collector/Collector.php b/app/code/Magento/Deploy/Collector/Collector.php
index b02c8e478d639..5974297a76cc7 100644
--- a/app/code/Magento/Deploy/Collector/Collector.php
+++ b/app/code/Magento/Deploy/Collector/Collector.php
@@ -8,6 +8,8 @@
use Magento\Deploy\Source\SourcePool;
use Magento\Deploy\Package\Package;
use Magento\Deploy\Package\PackageFactory;
+use Magento\Deploy\Package\PackageFile;
+use Magento\Framework\Module\Manager;
use Magento\Framework\View\Asset\PreProcessor\FileNameResolver;
/**
@@ -43,6 +45,9 @@ class Collector implements CollectorInterface
* @var PackageFactory
*/
private $packageFactory;
+
+ /** @var \Magento\Framework\Module\Manager */
+ private $moduleManager;
/**
* Default values for package primary identifiers
@@ -61,15 +66,19 @@ class Collector implements CollectorInterface
* @param SourcePool $sourcePool
* @param FileNameResolver $fileNameResolver
* @param PackageFactory $packageFactory
+ * @param Manager|null $moduleManager
*/
public function __construct(
SourcePool $sourcePool,
FileNameResolver $fileNameResolver,
- PackageFactory $packageFactory
+ PackageFactory $packageFactory,
+ Manager $moduleManager = null
) {
$this->sourcePool = $sourcePool;
$this->fileNameResolver = $fileNameResolver;
$this->packageFactory = $packageFactory;
+ $this->moduleManager = $moduleManager ?: \Magento\Framework\App\ObjectManager::getInstance()
+ ->get(\Magento\Framework\Module\Manager::class);
}
/**
@@ -81,19 +90,11 @@ public function collect()
foreach ($this->sourcePool->getAll() as $source) {
$files = $source->get();
foreach ($files as $file) {
- $file->setDeployedFileName($this->fileNameResolver->resolve($file->getFileName()));
- $params = [
- 'area' => $file->getArea(),
- 'theme' => $file->getTheme(),
- 'locale' => $file->getLocale(),
- 'module' => $file->getModule(),
- 'isVirtual' => (!$file->getLocale() || !$file->getTheme() || !$file->getArea())
- ];
- foreach ($this->packageDefaultValues as $name => $value) {
- if (!isset($params[$name])) {
- $params[$name] = $value;
- }
+ if ($file->getModule() && !$this->moduleManager->isEnabled($file->getModule())) {
+ continue;
}
+ $file->setDeployedFileName($this->fileNameResolver->resolve($file->getFileName()));
+ $params = $this->getParams($file);
$packagePath = "{$params['area']}/{$params['theme']}/{$params['locale']}";
if (!isset($packages[$packagePath])) {
$packages[$packagePath] = $this->packageFactory->create($params);
@@ -105,4 +106,27 @@ public function collect()
}
return $packages;
}
+
+ /**
+ * Retrieve package params
+ *
+ * @param PackageFile $file
+ * @return array
+ */
+ private function getParams(PackageFile $file)
+ {
+ $params = [
+ 'area' => $file->getArea(),
+ 'theme' => $file->getTheme(),
+ 'locale' => $file->getLocale(),
+ 'module' => $file->getModule(),
+ 'isVirtual' => (!$file->getLocale() || !$file->getTheme() || !$file->getArea())
+ ];
+ foreach ($this->packageDefaultValues as $name => $value) {
+ if (!isset($params[$name])) {
+ $params[$name] = $value;
+ }
+ }
+ return $params;
+ }
}
diff --git a/app/code/Magento/Deploy/Model/Filesystem.php b/app/code/Magento/Deploy/Model/Filesystem.php
index 59880d2d669b4..e7d4d31e6f144 100644
--- a/app/code/Magento/Deploy/Model/Filesystem.php
+++ b/app/code/Magento/Deploy/Model/Filesystem.php
@@ -29,8 +29,8 @@ class Filesystem
* Access permissions to the files are set during deploy Magento 2, directly after
* uploading code of Magento. Also it is possible to specify the value
* of inverse mask for setting access permissions to files generated by Magento.
- * @link http://devdocs.magento.com/guides/v2.0/install-gde/install/post-install-umask.html
- * @link http://devdocs.magento.com/guides/v2.0/install-gde/prereq/file-system-perms.html
+ * @link https://devdocs.magento.com/guides/v2.0/install-gde/install/post-install-umask.html
+ * @link https://devdocs.magento.com/guides/v2.0/install-gde/prereq/file-system-perms.html
*/
const PERMISSIONS_FILE = 0640;
@@ -41,8 +41,8 @@ class Filesystem
* Access permissions to the directories are set during deploy Magento 2, directly after
* uploading code of Magento. Also it is possible to specify the value
* of inverse mask for setting access permissions to directories generated by Magento.
- * @link http://devdocs.magento.com/guides/v2.0/install-gde/install/post-install-umask.html
- * @link http://devdocs.magento.com/guides/v2.0/install-gde/prereq/file-system-perms.html
+ * @link https://devdocs.magento.com/guides/v2.0/install-gde/install/post-install-umask.html
+ * @link https://devdocs.magento.com/guides/v2.0/install-gde/prereq/file-system-perms.html
*/
const PERMISSIONS_DIR = 0750;
@@ -320,8 +320,8 @@ public function cleanupFilesystem($directoryCodeList)
* Access permissions to the files and directories are set during deploy Magento 2, directly after
* uploading code of Magento. Also it is possible to specify the value
* of inverse mask for setting access permissions to files and directories generated by Magento.
- * @link http://devdocs.magento.com/guides/v2.0/install-gde/install/post-install-umask.html
- * @link http://devdocs.magento.com/guides/v2.0/install-gde/prereq/file-system-perms.html
+ * @link https://devdocs.magento.com/guides/v2.0/install-gde/install/post-install-umask.html
+ * @link https://devdocs.magento.com/guides/v2.0/install-gde/prereq/file-system-perms.html
*/
protected function changePermissions($directoryCodeList, $dirPermissions, $filePermissions)
{
@@ -345,8 +345,8 @@ protected function changePermissions($directoryCodeList, $dirPermissions, $fileP
* Access permissions to the files and directories are set during deploy Magento 2, directly after
* uploading code of Magento. Also it is possible to specify the value
* of inverse mask for setting access permissions to files and directories generated by Magento.
- * @link http://devdocs.magento.com/guides/v2.0/install-gde/install/post-install-umask.html
- * @link http://devdocs.magento.com/guides/v2.0/install-gde/prereq/file-system-perms.html
+ * @link https://devdocs.magento.com/guides/v2.0/install-gde/install/post-install-umask.html
+ * @link https://devdocs.magento.com/guides/v2.0/install-gde/prereq/file-system-perms.html
*/
public function lockStaticResources()
{
diff --git a/app/code/Magento/Directory/Model/Config/Source/WeightUnit.php b/app/code/Magento/Directory/Model/Config/Source/WeightUnit.php
index 4e2758b362a43..97d22633af03c 100644
--- a/app/code/Magento/Directory/Model/Config/Source/WeightUnit.php
+++ b/app/code/Magento/Directory/Model/Config/Source/WeightUnit.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Directory\Model\Config\Source;
/**
@@ -14,10 +15,23 @@
class WeightUnit implements \Magento\Framework\Option\ArrayInterface
{
/**
- * {@inheritdoc}
+ * @var string
+ */
+ const CODE_LBS = 'lbs';
+
+ /**
+ * @var string
+ */
+ const CODE_KGS = 'kgs';
+
+ /**
+ * @inheritdoc
*/
public function toOptionArray()
{
- return [['value' => 'lbs', 'label' => __('lbs')], ['value' => 'kgs', 'label' => __('kgs')]];
+ return [
+ ['value' => self::CODE_LBS, 'label' => __('lbs')],
+ ['value' => self::CODE_KGS, 'label' => __('kgs')]
+ ];
}
}
diff --git a/app/code/Magento/Directory/Model/PriceCurrency.php b/app/code/Magento/Directory/Model/PriceCurrency.php
index a211242d377f3..08d320abd8b53 100644
--- a/app/code/Magento/Directory/Model/PriceCurrency.php
+++ b/app/code/Magento/Directory/Model/PriceCurrency.php
@@ -46,7 +46,7 @@ public function __construct(
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function convert($amount, $scope = null, $currency = null)
{
@@ -58,17 +58,15 @@ public function convert($amount, $scope = null, $currency = null)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function convertAndRound($amount, $scope = null, $currency = null, $precision = self::DEFAULT_PRECISION)
{
- $currentCurrency = $this->getCurrency($scope, $currency);
- $convertedValue = $this->getStore($scope)->getBaseCurrency()->convert($amount, $currentCurrency);
- return round($convertedValue, $precision);
+ return $this->roundPrice($this->convert($amount, $scope, $currency), $precision);
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function format(
$amount,
@@ -82,7 +80,7 @@ public function format(
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function convertAndFormat(
$amount,
@@ -97,7 +95,7 @@ public function convertAndFormat(
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getCurrency($scope = null, $currency = null)
{
@@ -118,6 +116,8 @@ public function getCurrency($scope = null, $currency = null)
}
/**
+ * Get currrency symbol
+ *
* @param null|string|bool|int|\Magento\Framework\App\ScopeInterface $scope
* @param \Magento\Framework\Model\AbstractModel|string|null $currency
* @return string
@@ -148,13 +148,22 @@ protected function getStore($scope = null)
}
/**
- * Round price
+ * @inheritdoc
+ */
+ public function round($price)
+ {
+ return round($price, 2);
+ }
+
+ /**
+ * Round price with precision
*
* @param float $price
+ * @param int $precision
* @return float
*/
- public function round($price)
+ public function roundPrice($price, $precision = self::DEFAULT_PRECISION)
{
- return round($price, 2);
+ return round($price, $precision);
}
}
diff --git a/app/code/Magento/Directory/Model/ResourceModel/Country/Collection.php b/app/code/Magento/Directory/Model/ResourceModel/Country/Collection.php
index 20140baae01b6..f90a7b3b519b5 100644
--- a/app/code/Magento/Directory/Model/ResourceModel/Country/Collection.php
+++ b/app/code/Magento/Directory/Model/ResourceModel/Country/Collection.php
@@ -129,8 +129,8 @@ public function __construct(
/**
* Add top destinition countries to head of option array
*
- * @param $emptyLabel
- * @param $options
+ * @param string $emptyLabel
+ * @param array $options
* @return array
*/
private function addForegroundCountriesToOptionArray($emptyLabel, $options)
@@ -207,7 +207,7 @@ public function getItemById($countryId)
* Add filter by country code to collection.
* $countryCode can be either array of country codes or string representing one country code.
* $iso can be either array containing 'iso2', 'iso3' values or string with containing one of that values directly.
- * The collection will contain countries where at least one of contry $iso fields matches $countryCode.
+ * The collection will contain countries where at least one of country $iso fields matches $countryCode.
*
* @param string|string[] $countryCode
* @param string|string[] $iso
diff --git a/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/Crosssell.php b/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/Crosssell.php
index e7b1fff27680e..85f9ad39c4df5 100644
--- a/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/Crosssell.php
+++ b/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/Crosssell.php
@@ -6,6 +6,16 @@
*/
namespace Magento\Downloadable\Controller\Adminhtml\Downloadable\Product\Edit;
-class Crosssell extends \Magento\Catalog\Controller\Adminhtml\Product\Crosssell
+use Magento\Catalog\Controller\Adminhtml\Product\Crosssell as CatalogCrosssell;
+use Magento\Framework\App\Action\HttpPostActionInterface;
+
+/**
+ * Class Crosssell
+ *
+ * @package Magento\Downloadable\Controller\Adminhtml\Downloadable\Product\Edit
+ * @deprecated Not used since cross-sell products grid moved to UI components.
+ * @see Magento_Catalog::view/adminhtml/ui_component/crosssell_product_listing.xml
+ */
+class Crosssell extends CatalogCrosssell implements HttpPostActionInterface
{
}
diff --git a/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/CrosssellGrid.php b/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/CrosssellGrid.php
index 8a77cb2c01011..b6c25bdec9f14 100644
--- a/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/CrosssellGrid.php
+++ b/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/CrosssellGrid.php
@@ -6,6 +6,16 @@
*/
namespace Magento\Downloadable\Controller\Adminhtml\Downloadable\Product\Edit;
-class CrosssellGrid extends \Magento\Catalog\Controller\Adminhtml\Product\CrosssellGrid
+use Magento\Catalog\Controller\Adminhtml\Product\CrosssellGrid as CatalogCrosssellGrid;
+use Magento\Framework\App\Action\HttpPostActionInterface;
+
+/**
+ * Class CrosssellGrid
+ *
+ * @package Magento\Downloadable\Controller\Adminhtml\Downloadable\Product\Edit
+ * @deprecated Not used since cross-sell products grid moved to UI components.
+ * @see Magento_Catalog::view/adminhtml/ui_component/crosssell_product_listing.xml
+ */
+class CrosssellGrid extends CatalogCrosssellGrid implements HttpPostActionInterface
{
}
diff --git a/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/Related.php b/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/Related.php
index 8c10377f74e83..0352d97bca93a 100644
--- a/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/Related.php
+++ b/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/Related.php
@@ -6,6 +6,16 @@
*/
namespace Magento\Downloadable\Controller\Adminhtml\Downloadable\Product\Edit;
-class Related extends \Magento\Catalog\Controller\Adminhtml\Product\Related
+use Magento\Catalog\Controller\Adminhtml\Product\Related as CatalogRelated;
+use Magento\Framework\App\Action\HttpPostActionInterface;
+
+/**
+ * Class Related
+ *
+ * @package Magento\Downloadable\Controller\Adminhtml\Downloadable\Product\Edit
+ * @deprecated Not used since related products grid moved to UI components.
+ * @see Magento_Catalog::view/adminhtml/ui_component/related_product_listing.xml
+ */
+class Related extends CatalogRelated implements HttpPostActionInterface
{
}
diff --git a/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/RelatedGrid.php b/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/RelatedGrid.php
index 403b3828e39a0..11e9c4eb5e3af 100644
--- a/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/RelatedGrid.php
+++ b/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/RelatedGrid.php
@@ -6,6 +6,16 @@
*/
namespace Magento\Downloadable\Controller\Adminhtml\Downloadable\Product\Edit;
-class RelatedGrid extends \Magento\Catalog\Controller\Adminhtml\Product\RelatedGrid
+use Magento\Catalog\Controller\Adminhtml\Product\RelatedGrid as CatalogRelatedGrid;
+use Magento\Framework\App\Action\HttpPostActionInterface;
+
+/**
+ * Class RelatedGrid
+ *
+ * @package Magento\Downloadable\Controller\Adminhtml\Downloadable\Product\Edit
+ * @deprecated Not used since related products grid moved to UI components.
+ * @see Magento_Catalog::view/adminhtml/ui_component/related_product_listing.xml
+ */
+class RelatedGrid extends CatalogRelatedGrid implements HttpPostActionInterface
{
}
diff --git a/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/Upsell.php b/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/Upsell.php
index d712a7ec80d18..a8581f38c419b 100644
--- a/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/Upsell.php
+++ b/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/Upsell.php
@@ -6,6 +6,16 @@
*/
namespace Magento\Downloadable\Controller\Adminhtml\Downloadable\Product\Edit;
-class Upsell extends \Magento\Catalog\Controller\Adminhtml\Product\Upsell
+use Magento\Catalog\Controller\Adminhtml\Product\Upsell as CatalogUpsell;
+use Magento\Framework\App\Action\HttpPostActionInterface;
+
+/**
+ * Class Upsell
+ *
+ * @package Magento\Downloadable\Controller\Adminhtml\Downloadable\Product\Edit
+ * @deprecated Not used since upsell products grid moved to UI components.
+ * @see Magento_Catalog::view/adminhtml/ui_component/upsell_product_listing.xml
+ */
+class Upsell extends CatalogUpsell implements HttpPostActionInterface
{
}
diff --git a/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/UpsellGrid.php b/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/UpsellGrid.php
index 960909ca1b399..3e28a96948a77 100644
--- a/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/UpsellGrid.php
+++ b/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/Product/Edit/UpsellGrid.php
@@ -6,6 +6,16 @@
*/
namespace Magento\Downloadable\Controller\Adminhtml\Downloadable\Product\Edit;
-class UpsellGrid extends \Magento\Catalog\Controller\Adminhtml\Product\UpsellGrid
+use Magento\Catalog\Controller\Adminhtml\Product\UpsellGrid as CatalogUpsellGrid;
+use Magento\Framework\App\Action\HttpPostActionInterface;
+
+/**
+ * Class UpsellGrid
+ *
+ * @package Magento\Downloadable\Controller\Adminhtml\Downloadable\Product\Edit
+ * @deprecated Not used since upsell products grid moved to UI components.
+ * @see Magento_Catalog::view/adminhtml/ui_component/upsell_product_listing.xml
+ */
+class UpsellGrid extends CatalogUpsellGrid implements HttpPostActionInterface
{
}
diff --git a/app/code/Magento/Downloadable/Setup/Patch/Data/UpdateLinksExistDefaultAttributeValue.php b/app/code/Magento/Downloadable/Setup/Patch/Data/UpdateLinksExistDefaultAttributeValue.php
new file mode 100644
index 0000000000000..bde9accc39d79
--- /dev/null
+++ b/app/code/Magento/Downloadable/Setup/Patch/Data/UpdateLinksExistDefaultAttributeValue.php
@@ -0,0 +1,82 @@
+moduleDataSetup = $moduleDataSetup;
+ $this->eavSetupFactory = $eavSetupFactory;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function apply()
+ {
+ /** @var EavSetup $eavSetup */
+ $eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]);
+ // remove default value
+ $eavSetup->updateAttribute(
+ \Magento\Catalog\Model\Product::ENTITY,
+ 'links_exist',
+ 'default_value',
+ null
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public static function getDependencies()
+ {
+ return [InstallDownloadableAttributes::class];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public static function getVersion()
+ {
+ return '2.0.3';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getAliases()
+ {
+ return [];
+ }
+}
diff --git a/app/code/Magento/Eav/Api/AttributeSetRepositoryInterface.php b/app/code/Magento/Eav/Api/AttributeSetRepositoryInterface.php
index 8bd65ba85abef..be4b5a3fa0fe5 100644
--- a/app/code/Magento/Eav/Api/AttributeSetRepositoryInterface.php
+++ b/app/code/Magento/Eav/Api/AttributeSetRepositoryInterface.php
@@ -17,7 +17,7 @@ interface AttributeSetRepositoryInterface
* Retrieve list of Attribute Sets
*
* This call returns an array of objects, but detailed information about each object’s attributes might not be
- * included. See http://devdocs.magento.com/codelinks/attributes.html#AttributeSetRepositoryInterface to determine
+ * included. See https://devdocs.magento.com/codelinks/attributes.html#AttributeSetRepositoryInterface to determine
* which call to use to get detailed information about all attributes for an object.
*
* @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute.php b/app/code/Magento/Eav/Model/Entity/Attribute.php
index c605f3ce17e30..06a4abb985802 100644
--- a/app/code/Magento/Eav/Model/Entity/Attribute.php
+++ b/app/code/Magento/Eav/Model/Entity/Attribute.php
@@ -3,7 +3,6 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-
namespace Magento\Eav\Model\Entity;
use Magento\Framework\Api\AttributeValueFactory;
@@ -295,6 +294,12 @@ public function beforeSave()
}
}
+ if ($this->getFrontendInput() == 'media_image') {
+ if (!$this->getFrontendModel()) {
+ $this->setFrontendModel(\Magento\Catalog\Model\Product\Attribute\Frontend\Image::class);
+ }
+ }
+
if ($this->getBackendType() == 'gallery') {
if (!$this->getBackendModel()) {
$this->setBackendModel(\Magento\Eav\Model\Entity\Attribute\Backend\DefaultBackend::class);
@@ -316,7 +321,7 @@ public function afterSave()
}
/**
- * @return $this
+ * @inheritdoc
* @since 100.0.7
*/
public function afterDelete()
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
index 29ead9ff810e5..7ed455eccf4e0 100644
--- a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
+++ b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
namespace Magento\Eav\Model\Entity\Attribute;
@@ -418,7 +419,7 @@ public function getIsVisibleOnFront()
/**
* Returns default value
*
- * @return string|int|bool|float
+ * @return string|null
* @codeCoverageIgnore
*/
public function getDefaultValue()
@@ -812,7 +813,7 @@ public function getBackendTable()
if ($this->isStatic()) {
$this->_dataTable = $this->getEntityType()->getValueTablePrefix();
} else {
- $backendTable = trim($this->_getData('backend_table'));
+ $backendTable = trim((string)$this->_getData('backend_table'));
if (empty($backendTable)) {
$entityTable = [$this->getEntityType()->getEntityTablePrefix(), $this->getBackendType()];
$backendTable = $this->getResource()->getTable($entityTable);
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Backend/AbstractBackend.php b/app/code/Magento/Eav/Model/Entity/Attribute/Backend/AbstractBackend.php
index 53c32e1cbfba4..86ab6f6ea6e9f 100644
--- a/app/code/Magento/Eav/Model/Entity/Attribute/Backend/AbstractBackend.php
+++ b/app/code/Magento/Eav/Model/Entity/Attribute/Backend/AbstractBackend.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
namespace Magento\Eav\Model\Entity\Attribute\Backend;
@@ -204,12 +205,12 @@ public function getEntityValueId($entity)
/**
* Retrieve default value
*
- * @return mixed
+ * @return string
*/
public function getDefaultValue()
{
if ($this->_defaultValue === null) {
- if ($this->getAttribute()->getDefaultValue()) {
+ if ($this->getAttribute()->getDefaultValue() !== null) {
$this->_defaultValue = $this->getAttribute()->getDefaultValue();
} else {
$this->_defaultValue = "";
@@ -285,7 +286,7 @@ public function afterLoad($object)
public function beforeSave($object)
{
$attrCode = $this->getAttribute()->getAttributeCode();
- if (!$object->hasData($attrCode) && $this->getDefaultValue()) {
+ if (!$object->hasData($attrCode) && $this->getDefaultValue() !== '') {
$object->setData($attrCode, $this->getDefaultValue());
}
@@ -348,9 +349,8 @@ public function getAffectedFields($object)
}
/**
- * By default attribute value is considered scalar that can be stored in a generic way
+ * By default attribute value is considered scalar that can be stored in a generic way {@inheritdoc}
*
- * {@inheritdoc}
* @codeCoverageIgnore
*/
public function isScalar()
diff --git a/app/code/Magento/Elasticsearch/etc/di.xml b/app/code/Magento/Elasticsearch/etc/di.xml
index b10d30e48064a..6a42e4b3c9fe2 100644
--- a/app/code/Magento/Elasticsearch/etc/di.xml
+++ b/app/code/Magento/Elasticsearch/etc/di.xml
@@ -416,7 +416,7 @@
- - 2147483647
+ - 10000
- 2147483647
diff --git a/app/code/Magento/Email/Model/Transport.php b/app/code/Magento/Email/Model/Transport.php
index 28d2d171eecf4..90a4e6571c9b6 100644
--- a/app/code/Magento/Email/Model/Transport.php
+++ b/app/code/Magento/Email/Model/Transport.php
@@ -87,7 +87,7 @@ public function __construct(
public function sendMessage()
{
try {
- $zendMessage = Message::fromString($this->message->getRawMessage());
+ $zendMessage = Message::fromString($this->message->getRawMessage())->setEncoding('utf-8');
if (2 === $this->isSetReturnPath && $this->returnPathValue) {
$zendMessage->setSender($this->returnPathValue);
} elseif (1 === $this->isSetReturnPath && $zendMessage->getFrom()->count()) {
diff --git a/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php b/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php
index aae30026b2b77..9331b68675b67 100644
--- a/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php
+++ b/app/code/Magento/EncryptionKey/Setup/Patch/Data/SodiumChachaPatch.php
@@ -8,12 +8,18 @@
namespace Magento\EncryptionKey\Setup\Patch\Data;
use Magento\Framework\Setup\Patch\DataPatchInterface;
+use Magento\Framework\App\ObjectManager;
/**
* Migrate encrypted configuration values to the latest cipher
*/
class SodiumChachaPatch implements DataPatchInterface
{
+ /**
+ * @var \Magento\Framework\Config\ScopeInterface
+ */
+ private $scope;
+
/**
* @var \Magento\Framework\Setup\ModuleDataSetupInterface
*/
@@ -35,25 +41,29 @@ class SodiumChachaPatch implements DataPatchInterface
private $state;
/**
+ * SodiumChachaPatch constructor.
* @param \Magento\Framework\Setup\ModuleDataSetupInterface $moduleDataSetup
* @param \Magento\Config\Model\Config\Structure\Proxy $structure
* @param \Magento\Framework\Encryption\EncryptorInterface $encryptor
* @param \Magento\Framework\App\State $state
+ * @param \Magento\Framework\Config\ScopeInterface|null $scope
*/
public function __construct(
\Magento\Framework\Setup\ModuleDataSetupInterface $moduleDataSetup,
\Magento\Config\Model\Config\Structure\Proxy $structure,
\Magento\Framework\Encryption\EncryptorInterface $encryptor,
- \Magento\Framework\App\State $state
+ \Magento\Framework\App\State $state,
+ \Magento\Framework\Config\ScopeInterface $scope = null
) {
$this->moduleDataSetup = $moduleDataSetup;
$this->structure = $structure;
$this->encryptor = $encryptor;
$this->state = $state;
+ $this->scope = $scope ?? ObjectManager::getInstance()->get(\Magento\Framework\Config\ScopeInterface::class);
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function apply()
{
@@ -65,7 +75,7 @@ public function apply()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public static function getDependencies()
{
@@ -73,41 +83,67 @@ public static function getDependencies()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getAliases()
{
return [];
}
+ /**
+ * Re encrypt sensitive data in the system configuration
+ */
private function reEncryptSystemConfigurationValues()
{
- $structure = $this->structure;
- $paths = $this->state->emulateAreaCode(
- \Magento\Framework\App\Area::AREA_ADMINHTML,
- function () use ($structure) {
- return $structure->getFieldPathsByAttribute(
- 'backend_model',
- \Magento\Config\Model\Config\Backend\Encrypted::class
- );
- }
+ $table = $this->moduleDataSetup->getTable('core_config_data');
+ $hasEncryptedData = $this->moduleDataSetup->getConnection()->fetchOne(
+ $this->moduleDataSetup->getConnection()
+ ->select()
+ ->from($table, [new \Zend_Db_Expr('count(value)')])
+ ->where('value LIKE ?', '0:2%')
);
- // walk through found data and re-encrypt it
- if ($paths) {
- $table = $this->moduleDataSetup->getTable('core_config_data');
- $values = $this->moduleDataSetup->getConnection()->fetchPairs(
- $this->moduleDataSetup->getConnection()
- ->select()
- ->from($table, ['config_id', 'value'])
- ->where('path IN (?)', $paths)
- ->where('value NOT LIKE ?', '')
+ if ($hasEncryptedData !== '0') {
+ $currentScope = $this->scope->getCurrentScope();
+ $structure = $this->structure;
+ $paths = $this->state->emulateAreaCode(
+ \Magento\Framework\App\Area::AREA_ADMINHTML,
+ function () use ($structure) {
+ $this->scope->setCurrentScope(\Magento\Framework\App\Area::AREA_ADMINHTML);
+ /** Returns list of structure paths to be re encrypted */
+ $paths = $structure->getFieldPathsByAttribute(
+ 'backend_model',
+ \Magento\Config\Model\Config\Backend\Encrypted::class
+ );
+ /** Returns list of mapping between configPath => [structurePaths] */
+ $mappedPaths = $structure->getFieldPaths();
+ foreach ($mappedPaths as $mappedPath => $data) {
+ foreach ($data as $structurePath) {
+ if ($structurePath !== $mappedPath && $key = array_search($structurePath, $paths)) {
+ $paths[$key] = $mappedPath;
+ }
+ }
+ }
+
+ return array_unique($paths);
+ }
);
- foreach ($values as $configId => $value) {
- $this->moduleDataSetup->getConnection()->update(
- $table,
- ['value' => $this->encryptor->encrypt($this->encryptor->decrypt($value))],
- ['config_id = ?' => (int)$configId]
+ $this->scope->setCurrentScope($currentScope);
+ // walk through found data and re-encrypt it
+ if ($paths) {
+ $values = $this->moduleDataSetup->getConnection()->fetchPairs(
+ $this->moduleDataSetup->getConnection()
+ ->select()
+ ->from($table, ['config_id', 'value'])
+ ->where('path IN (?)', $paths)
+ ->where('value NOT LIKE ?', '')
);
+ foreach ($values as $configId => $value) {
+ $this->moduleDataSetup->getConnection()->update(
+ $table,
+ ['value' => $this->encryptor->encrypt($this->encryptor->decrypt($value))],
+ ['config_id = ?' => (int)$configId]
+ );
+ }
}
}
}
diff --git a/app/code/Magento/Fedex/Model/Carrier.php b/app/code/Magento/Fedex/Model/Carrier.php
index f6e63e04ac559..955345851e67a 100644
--- a/app/code/Magento/Fedex/Model/Carrier.php
+++ b/app/code/Magento/Fedex/Model/Carrier.php
@@ -986,6 +986,7 @@ public function getCode($type, $code = '')
* Return FeDex currency ISO code by Magento Base Currency Code
*
* @return string 3-digit currency code
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function getCurrencyCode()
{
@@ -1008,7 +1009,7 @@ public function getCurrencyCode()
];
$currencyCode = $this->_storeManager->getStore()->getBaseCurrencyCode();
- return isset($codes[$currencyCode]) ? $codes[$currencyCode] : $currencyCode;
+ return $codes[$currencyCode] ?? $currencyCode;
}
/**
@@ -1438,6 +1439,8 @@ protected function _doShipmentRequest(\Magento\Framework\DataObject $request)
}
/**
+ * Return Tracking Number
+ *
* @param array|object $trackingIds
* @return string
*/
@@ -1452,10 +1455,10 @@ function ($val) {
}
/**
- * For multi package shipments. Delete requested shipments if the current shipment
- * request is failed
+ * For multi package shipments. Delete requested shipments if the current shipment request is failed
*
* @param array $data
+ *
* @return bool
*/
public function rollBack($data)
@@ -1475,6 +1478,7 @@ public function rollBack($data)
* Return container types of carrier
*
* @param \Magento\Framework\DataObject|null $params
+ *
* @return array|bool
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
@@ -1542,6 +1546,7 @@ public function getContainerTypesFilter()
* Return delivery confirmation types of carrier
*
* @param \Magento\Framework\DataObject|null $params
+ *
* @return array
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
@@ -1552,6 +1557,7 @@ public function getDeliveryConfirmationTypes(\Magento\Framework\DataObject $para
/**
* Recursive replace sensitive fields in debug data by the mask
+ *
* @param string $data
* @return string
*/
@@ -1569,6 +1575,7 @@ protected function filterDebugData($data)
/**
* Parse track details response from Fedex
+ *
* @param \stdClass $trackInfo
* @return array
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
@@ -1639,6 +1646,7 @@ private function processTrackingDetails(\stdClass $trackInfo)
/**
* Parse delivery datetime from tracking details
+ *
* @param \stdClass $trackInfo
* @return \Datetime|null
*/
@@ -1655,8 +1663,7 @@ private function getDeliveryDateTime(\stdClass $trackInfo)
}
/**
- * Get delivery address details in string representation
- * Return City, State, Country Code
+ * Get delivery address details in string representation Return City, State, Country Code
*
* @param \stdClass $address
* @return \Magento\Framework\Phrase|string
@@ -1718,6 +1725,7 @@ private function processTrackDetailsEvents(array $events)
/**
* Append error message to rate result instance
+ *
* @param string $trackingValue
* @param string $errorMessage
*/
@@ -1759,8 +1767,7 @@ private function parseDate($timestamp)
}
/**
- * Defines payment type by request.
- * Two values are available: RECIPIENT or SENDER.
+ * Defines payment type by request. Two values are available: RECIPIENT or SENDER.
*
* @param DataObject $request
* @return string
diff --git a/app/code/Magento/Fedex/etc/wsdl/RateService_v10.wsdl b/app/code/Magento/Fedex/etc/wsdl/RateService_v10.wsdl
index 6ae04ccee0775..62795f07239a6 100644
--- a/app/code/Magento/Fedex/etc/wsdl/RateService_v10.wsdl
+++ b/app/code/Magento/Fedex/etc/wsdl/RateService_v10.wsdl
@@ -355,7 +355,7 @@
- Shows the specific combination of service options combined with the service type that produced this committment in the set returned to the caller.
+ Shows the specific combination of service options combined with the service type that produced this commitment in the set returned to the caller.
@@ -473,7 +473,7 @@
For international multiple piece shipments, commodity information must be passed in the Master and on each child transaction.
- If this shipment cotains more than four commodity line items, the four highest valued should be included in the first 4 occurrences for this request.
+ If this shipment contains more than four commodity line items, the four highest valued should be included in the first 4 occurrences for this request.
@@ -916,7 +916,7 @@
- For customers producing their own Ground labels, this field specifies which secondary barcode will be printed on the label; so that the primary barcode produced by FedEx has the corect SCNC.
+ For customers producing their own Ground labels, this field specifies which secondary barcode will be printed on the label; so that the primary barcode produced by FedEx has the correct SCNC.
@@ -1006,7 +1006,7 @@
For international multiple piece shipments, commodity information must be passed in the Master and on each child transaction.
- If this shipment cotains more than four commodity line items, the four highest valued should be included in the first 4 occurrences for this request.
+ If this shipment contains more than four commodity line items, the four highest valued should be included in the first 4 occurrences for this request.
@@ -1151,7 +1151,7 @@
- Data required to complete the Destionation Control Statement for US exports.
+ Data required to complete the Destination Control Statement for US exports.
@@ -2278,7 +2278,7 @@
- For thermal printer lables this indicates the size of the label and the location of the doc tab if present.
+ For thermal printer labels this indicates the size of the label and the location of the doc tab if present.
@@ -2780,7 +2780,7 @@
- The descriptive data for a person or company entitiy doing business with FedEx.
+ The descriptive data for a person or company entity doing business with FedEx.
@@ -3069,7 +3069,7 @@
- Shows the specific combination of service options combined with the service type that produced this committment in the set returned to the caller.
+ Shows the specific combination of service options combined with the service type that produced this commitment in the set returned to the caller.
@@ -3195,7 +3195,7 @@
- If requesting rates using the PackageDetails element (one package at a time) in the request, the rates for each package will be returned in this element. Currently total piece total weight rates are also retuned in this element.
+ If requesting rates using the PackageDetails element (one package at a time) in the request, the rates for each package will be returned in this element. Currently total piece total weight rates are also returned in this element.
@@ -3416,7 +3416,7 @@
- Identifies the total weight of the shipment being conveyed to FedEx.This is only applicable to International shipments and should only be used on the first package of a mutiple piece shipment.This value contains 1 explicit decimal position
+ Identifies the total weight of the shipment being conveyed to FedEx.This is only applicable to International shipments and should only be used on the first package of a multiple piece shipment.This value contains 1 explicit decimal position
@@ -3582,7 +3582,7 @@
- The type of return shipment that is being requested. At present the only type of retrun shipment that is supported is PRINT_RETURN_LABEL. With this option you can print a return label to insert into the box of an outbound shipment. This option can not be used to print an outbound label.
+ The type of return shipment that is being requested. At present the only type of return shipment that is supported is PRINT_RETURN_LABEL. With this option you can print a return label to insert into the box of an outbound shipment. This option can not be used to print an outbound label.
@@ -3655,7 +3655,7 @@
- These values control the optional features of service that may be combined in a commitment/rate comparision transaction.
+ These values control the optional features of service that may be combined in a commitment/rate comparison transaction.
@@ -4731,7 +4731,7 @@
- The calculated varibale handling charge plus the net charge.
+ The calculated variable handling charge plus the net charge.
diff --git a/app/code/Magento/Fedex/etc/wsdl/RateService_v9.wsdl b/app/code/Magento/Fedex/etc/wsdl/RateService_v9.wsdl
index efbf75076c2cb..17a6f74cc09b8 100644
--- a/app/code/Magento/Fedex/etc/wsdl/RateService_v9.wsdl
+++ b/app/code/Magento/Fedex/etc/wsdl/RateService_v9.wsdl
@@ -354,7 +354,7 @@
- Shows the specific combination of service options combined with the service type that produced this committment in the set returned to the caller.
+ Shows the specific combination of service options combined with the service type that produced this commitment in the set returned to the caller.
@@ -472,7 +472,7 @@
For international multiple piece shipments, commodity information must be passed in the Master and on each child transaction.
- If this shipment cotains more than four commodity line items, the four highest valued should be included in the first 4 occurrences for this request.
+ If this shipment commitment more than four commodity line items, the four highest valued should be included in the first 4 occurrences for this request.
@@ -1006,7 +1006,7 @@
For international multiple piece shipments, commodity information must be passed in the Master and on each child transaction.
- If this shipment cotains more than four commodity line items, the four highest valued should be included in the first 4 occurrences for this request.
+ If this shipment contains more than four commodity line items, the four highest valued should be included in the first 4 occurrences for this request.
@@ -1148,7 +1148,7 @@
- Data required to complete the Destionation Control Statement for US exports.
+ Data required to complete the Destination Control Statement for US exports.
@@ -2313,7 +2313,7 @@
- For thermal printer lables this indicates the size of the label and the location of the doc tab if present.
+ For thermal printer labels this indicates the size of the label and the location of the doc tab if present.
@@ -3099,7 +3099,7 @@
- Shows the specific combination of service options combined with the service type that produced this committment in the set returned to the caller.
+ Shows the specific combination of service options combined with the service type that produced this commitment in the set returned to the caller.
@@ -3216,7 +3216,7 @@
- If requesting rates using the PackageDetails element (one package at a time) in the request, the rates for each package will be returned in this element. Currently total piece total weight rates are also retuned in this element.
+ If requesting rates using the PackageDetails element (one package at a time) in the request, the rates for each package will be returned in this element. Currently total piece total weight rates are also returned in this element.
@@ -3622,7 +3622,7 @@
- The type of return shipment that is being requested. At present the only type of retrun shipment that is supported is PRINT_RETURN_LABEL. With this option you can print a return label to insert into the box of an outbound shipment. This option can not be used to print an outbound label.
+ The type of return shipment that is being requested. At present the only type of return shipment that is supported is PRINT_RETURN_LABEL. With this option you can print a return label to insert into the box of an outbound shipment. This option can not be used to print an outbound label.
@@ -3687,7 +3687,7 @@
- These values control the optional features of service that may be combined in a commitment/rate comparision transaction.
+ These values control the optional features of service that may be combined in a commitment/rate comparison transaction.
@@ -4611,7 +4611,7 @@
- The calculated varibale handling charge plus the net charge.
+ The calculated variable handling charge plus the net charge.
diff --git a/app/code/Magento/Fedex/etc/wsdl/ShipService_v10.wsdl b/app/code/Magento/Fedex/etc/wsdl/ShipService_v10.wsdl
index 6c43af98ef682..54bb57d490c76 100644
--- a/app/code/Magento/Fedex/etc/wsdl/ShipService_v10.wsdl
+++ b/app/code/Magento/Fedex/etc/wsdl/ShipService_v10.wsdl
@@ -498,7 +498,7 @@
For international multiple piece shipments, commodity information must be passed in the Master and on each child transaction.
- If this shipment cotains more than four commodity line items, the four highest valued should be included in the first 4 occurrences for this request.
+ If this shipment contains more than four commodity line items, the four highest valued should be included in the first 4 occurrences for this request.
@@ -1515,12 +1515,12 @@
- Data required to complete the Destionation Control Statement for US exports.
+ Data required to complete the Destination Control Statement for US exports.
- List of applicable Statment types.
+ List of applicable Statement types.
@@ -2524,7 +2524,7 @@
- For thermal printer lables this indicates the size of the label and the location of the doc tab if present.
+ For thermal printer labels this indicates the size of the label and the location of the doc tab if present.
@@ -5225,7 +5225,7 @@
- The calculated varibale handling charge plus the net charge.
+ The calculated variable handling charge plus the net charge.
diff --git a/app/code/Magento/Fedex/etc/wsdl/ShipService_v9.wsdl b/app/code/Magento/Fedex/etc/wsdl/ShipService_v9.wsdl
index 8c11a5f660351..d8dc0fdfed4ab 100644
--- a/app/code/Magento/Fedex/etc/wsdl/ShipService_v9.wsdl
+++ b/app/code/Magento/Fedex/etc/wsdl/ShipService_v9.wsdl
@@ -498,7 +498,7 @@
For international multiple piece shipments, commodity information must be passed in the Master and on each child transaction.
- If this shipment cotains more than four commodity line items, the four highest valued should be included in the first 4 occurrences for this request.
+ If this shipment contains more than four commodity line items, the four highest valued should be included in the first 4 occurrences for this request.
@@ -1515,12 +1515,12 @@
- Data required to complete the Destionation Control Statement for US exports.
+ Data required to complete the Destination Control Statement for US exports.
- List of applicable Statment types.
+ List of applicable Statement types.
@@ -2524,7 +2524,7 @@
- For thermal printer lables this indicates the size of the label and the location of the doc tab if present.
+ For thermal printer labels this indicates the size of the label and the location of the doc tab if present.
@@ -5225,7 +5225,7 @@
- The calculated varibale handling charge plus the net charge.
+ The calculated variable handling charge plus the net charge.
diff --git a/app/code/Magento/GiftMessage/Test/Mftf/Data/GiftOptionsData.xml b/app/code/Magento/GiftMessage/Test/Mftf/Data/GiftOptionsData.xml
index 951bbd3b787b7..bb3f566f1cbc6 100644
--- a/app/code/Magento/GiftMessage/Test/Mftf/Data/GiftOptionsData.xml
+++ b/app/code/Magento/GiftMessage/Test/Mftf/Data/GiftOptionsData.xml
@@ -7,23 +7,21 @@
-->
-
- 0
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd">
+
+ AllowGiftMessageForOrder
-
- 0
+
+ 1
-
-
- defaultWrappingAllowOrder
- defaultAllowGiftReceipt
- defaultAllowPrintedCard
+
+ OrderDefault
+ ItemsDefault
-
- 1
+
+ 0
-
- 1
+
+ 0
diff --git a/app/code/Magento/GiftMessage/Test/Mftf/Metadata/gift_options-meta.xml b/app/code/Magento/GiftMessage/Test/Mftf/Metadata/gift_options-meta.xml
new file mode 100644
index 0000000000000..d4b7997e77d15
--- /dev/null
+++ b/app/code/Magento/GiftMessage/Test/Mftf/Metadata/gift_options-meta.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+ string
+
+
+ string
+
+
+
+
+
+
diff --git a/app/code/Magento/GroupedCatalogInventory/LICENSE.txt b/app/code/Magento/GroupedCatalogInventory/LICENSE.txt
new file mode 100644
index 0000000000000..49525fd99da9c
--- /dev/null
+++ b/app/code/Magento/GroupedCatalogInventory/LICENSE.txt
@@ -0,0 +1,48 @@
+
+Open Software License ("OSL") v. 3.0
+
+This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work:
+
+Licensed under the Open Software License version 3.0
+
+ 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following:
+
+ 1. to reproduce the Original Work in copies, either alone or as part of a collective work;
+
+ 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work;
+
+ 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License;
+
+ 4. to perform the Original Work publicly; and
+
+ 5. to display the Original Work publicly.
+
+ 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works.
+
+ 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work.
+
+ 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license.
+
+ 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c).
+
+ 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
+
+ 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer.
+
+ 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation.
+
+ 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c).
+
+ 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
+
+ 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License.
+
+ 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
+
+ 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
+
+ 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+ 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
+
+ 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.
\ No newline at end of file
diff --git a/app/code/Magento/GroupedCatalogInventory/LICENSE_AFL.txt b/app/code/Magento/GroupedCatalogInventory/LICENSE_AFL.txt
new file mode 100644
index 0000000000000..f39d641b18a19
--- /dev/null
+++ b/app/code/Magento/GroupedCatalogInventory/LICENSE_AFL.txt
@@ -0,0 +1,48 @@
+
+Academic Free License ("AFL") v. 3.0
+
+This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work:
+
+Licensed under the Academic Free License version 3.0
+
+ 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following:
+
+ 1. to reproduce the Original Work in copies, either alone or as part of a collective work;
+
+ 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work;
+
+ 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License;
+
+ 4. to perform the Original Work publicly; and
+
+ 5. to display the Original Work publicly.
+
+ 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works.
+
+ 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work.
+
+ 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license.
+
+ 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c).
+
+ 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
+
+ 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer.
+
+ 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation.
+
+ 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c).
+
+ 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
+
+ 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License.
+
+ 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
+
+ 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
+
+ 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+ 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
+
+ 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.
diff --git a/app/code/Magento/GroupedCatalogInventory/Plugin/OutOfStockFilter.php b/app/code/Magento/GroupedCatalogInventory/Plugin/OutOfStockFilter.php
new file mode 100644
index 0000000000000..599f40deddde0
--- /dev/null
+++ b/app/code/Magento/GroupedCatalogInventory/Plugin/OutOfStockFilter.php
@@ -0,0 +1,88 @@
+stockStatusRepository = $stockStatusRepository;
+ $this->criteriaInterfaceFactory = $criteriaInterfaceFactory;
+ }
+
+ /**
+ * Removes out of stock products for requests that don't specify the super group
+ *
+ * @param Grouped $subject
+ * @param array|string $result
+ * @param \Magento\Framework\DataObject $buyRequest
+ * @return string|array
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterPrepareForCartAdvanced(
+ Grouped $subject,
+ $result,
+ \Magento\Framework\DataObject $buyRequest
+ ) {
+ if (!is_array($result) && $result instanceof Product) {
+ $result = [$result];
+ }
+
+ // Only remove out-of-stock products if no quantities were specified
+ if (is_array($result) && !empty($result) && !$buyRequest->getData('super_group')) {
+ $productIds = [];
+ $productIdMap = [];
+
+ foreach ($result as $index => $cartItem) {
+ $productIds[] = $cartItem->getId();
+ $productIdMap[$cartItem->getId()] = $index;
+ }
+
+ $criteria = $this->criteriaInterfaceFactory->create();
+ $criteria->setProductsFilter($productIds);
+
+ $stockStatusCollection = $this->stockStatusRepository->getList($criteria);
+ foreach ($stockStatusCollection->getItems() as $status) {
+ /** @var $status StockStatusInterface */
+ if ($status->getStockStatus() == StockStatusInterface::STATUS_OUT_OF_STOCK) {
+ unset($result[$productIdMap[$status->getProductId()]]);
+ }
+ }
+
+ unset($productIdMap);
+ }
+
+ return $result;
+ }
+}
diff --git a/app/code/Magento/GroupedCatalogInventory/README.md b/app/code/Magento/GroupedCatalogInventory/README.md
new file mode 100644
index 0000000000000..c229c7e9919ad
--- /dev/null
+++ b/app/code/Magento/GroupedCatalogInventory/README.md
@@ -0,0 +1 @@
+Magento_GroupedCatalogInventory contains behavior related to the inventory status of items within grouped products.
diff --git a/app/code/Magento/GroupedCatalogInventory/Test/Unit/Plugin/OutOfStockFilterTest.php b/app/code/Magento/GroupedCatalogInventory/Test/Unit/Plugin/OutOfStockFilterTest.php
new file mode 100644
index 0000000000000..07877938e36ae
--- /dev/null
+++ b/app/code/Magento/GroupedCatalogInventory/Test/Unit/Plugin/OutOfStockFilterTest.php
@@ -0,0 +1,221 @@
+searchCriteriaMock->expects($this->never())->method('setProductsFilter');
+ $this->stockStatusRepositoryMock->expects($this->never())->method('getList');
+
+ $plugin = $this->getPluginInstance();
+
+ $result = $plugin->afterPrepareForCartAdvanced(
+ $this->subjectMock,
+ $nonArrayResult,
+ new DataObject()
+ );
+
+ $this->assertSame($nonArrayResult, $result);
+ }
+
+ public function testFilterIgnoresResultIfSuperGroupIsPresent()
+ {
+ $this->searchCriteriaMock->expects($this->never())->method('setProductsFilter');
+ $this->stockStatusRepositoryMock->expects($this->never())->method('getList');
+
+ $plugin = $this->getPluginInstance();
+
+ $product = $this->createProductMock();
+
+ $result = $plugin->afterPrepareForCartAdvanced(
+ $this->subjectMock,
+ [$product],
+ new DataObject(['super_group' => [123 => '1']])
+ );
+
+ $this->assertSame([$product], $result);
+ }
+
+ /**
+ * @param $originalResult
+ * @param $stockStatusCollection
+ * @param $expectedResult
+ * @dataProvider outOfStockProductData
+ */
+ public function testFilterRemovesOutOfStockProductsWhenSuperGroupIsNotPresent(
+ $originalResult,
+ $stockStatusCollection,
+ $expectedResult
+ ) {
+ $this->stockStatusRepositoryMock
+ ->expects($this->once())
+ ->method('getList')
+ ->with($this->searchCriteriaMock)
+ ->willReturn($stockStatusCollection);
+
+ $plugin = $this->getPluginInstance();
+
+ $result = $plugin->afterPrepareForCartAdvanced(
+ $this->subjectMock,
+ $originalResult,
+ new DataObject()
+ );
+
+ $this->assertSame($expectedResult, $result);
+ }
+
+ public function outOfStockProductData()
+ {
+ $product1 = $this->createProductMock();
+ $product1->method('getId')->willReturn(123);
+
+ $product2 = $this->createProductMock();
+ $product2->method('getId')->willReturn(321);
+
+ return [
+ [[$product1, $product2], $this->createStatusResult([123 => false, 321 => true]), [1 => $product2]],
+ [[$product1], $this->createStatusResult([123 => true]), [0 => $product1]],
+ [$product1, $this->createStatusResult([123 => true]), [0 => $product1]]
+ ];
+ }
+
+ public function nonArrayResults()
+ {
+ return [
+ [123],
+ ['abc'],
+ [new \stdClass()]
+ ];
+ }
+
+ protected function setUp()
+ {
+ $this->subjectMock = $this->getMockBuilder(Grouped::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->stockStatusRepositoryMock = $this->getMockBuilder(StockStatusRepositoryInterface::class)
+ ->getMock();
+
+ $this->searchCriteriaFactoryMock = $this->getMockBuilder(StockStatusCriteriaInterfaceFactory::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->searchCriteriaMock = $this->getMockBuilder(StockStatusCriteriaInterface::class)
+ ->getMock();
+
+ $this->stockStatusCollectionMock = $this->getMockBuilder(StockStatusCollectionInterface::class)
+ ->getMock();
+
+ $this->searchCriteriaFactoryMock
+ ->expects($this->any())
+ ->method('create')
+ ->willReturn($this->searchCriteriaMock);
+ }
+
+ private function createProductMock()
+ {
+ return $this->getMockBuilder(Product::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ /**
+ * @return OutOfStockFilter
+ */
+ private function getPluginInstance()
+ {
+ $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+
+ /** @var OutOfStockFilter $filter */
+ $filter = $objectManager->getObject(OutOfStockFilter::class, [
+ 'stockStatusRepository' => $this->stockStatusRepositoryMock,
+ 'criteriaInterfaceFactory' => $this->searchCriteriaFactoryMock
+ ]);
+
+ return $filter;
+ }
+
+ private function createStatusResult(array $productStatuses)
+ {
+ $result = [];
+
+ foreach ($productStatuses as $productId => $status) {
+ $mock = $this->getMockBuilder(StockStatusInterface::class)
+ ->getMock();
+
+ $mock->expects($this->any())
+ ->method('getProductId')
+ ->willReturn($productId);
+
+ $mock->expects($this->any())
+ ->method('getStockStatus')
+ ->willReturn(
+ $status
+ ? StockStatusInterface::STATUS_IN_STOCK
+ : StockStatusInterface::STATUS_OUT_OF_STOCK
+ );
+
+ $result[] = $mock;
+ }
+
+ $stockStatusCollection = $this->getMockBuilder(StockStatusCollectionInterface::class)
+ ->getMock();
+
+ $stockStatusCollection
+ ->expects($this->once())
+ ->method('getItems')
+ ->willReturn($result);
+
+ return $stockStatusCollection;
+ }
+}
diff --git a/app/code/Magento/GroupedCatalogInventory/composer.json b/app/code/Magento/GroupedCatalogInventory/composer.json
new file mode 100644
index 0000000000000..c9946bc3f36ac
--- /dev/null
+++ b/app/code/Magento/GroupedCatalogInventory/composer.json
@@ -0,0 +1,27 @@
+{
+ "name": "magento/module-grouped-catalog-inventory",
+ "description": "N/A",
+ "config": {
+ "sort-packages": true
+ },
+ "require": {
+ "php": "~7.1.3||~7.2.0",
+ "magento/framework": "*",
+ "magento/module-catalog": "*",
+ "magento/module-catalog-inventory": "*",
+ "magento/module-grouped-product": "*"
+ },
+ "type": "magento2-module",
+ "license": [
+ "OSL-3.0",
+ "AFL-3.0"
+ ],
+ "autoload": {
+ "files": [
+ "registration.php"
+ ],
+ "psr-4": {
+ "Magento\\GroupedCatalogInventory\\": ""
+ }
+ }
+}
diff --git a/app/code/Magento/GroupedCatalogInventory/etc/di.xml b/app/code/Magento/GroupedCatalogInventory/etc/di.xml
new file mode 100644
index 0000000000000..f38a9e845a159
--- /dev/null
+++ b/app/code/Magento/GroupedCatalogInventory/etc/di.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/app/code/Magento/GroupedCatalogInventory/etc/module.xml b/app/code/Magento/GroupedCatalogInventory/etc/module.xml
new file mode 100644
index 0000000000000..246f10ff67934
--- /dev/null
+++ b/app/code/Magento/GroupedCatalogInventory/etc/module.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/GroupedCatalogInventory/registration.php b/app/code/Magento/GroupedCatalogInventory/registration.php
new file mode 100644
index 0000000000000..8899a48edf6d5
--- /dev/null
+++ b/app/code/Magento/GroupedCatalogInventory/registration.php
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php b/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php
index 4f4ed9de03e43..8721dd05a0fa6 100644
--- a/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php
+++ b/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php
@@ -244,7 +244,7 @@ protected function _getSelectHtmlWithValue(Attribute $attribute, $value)
$options = $attribute->getSource()->getAllOptions(false);
}
if ($size = count($options)) {
- // add empty vaue option
+ // add empty value option
$firstOption = reset($options);
if ('' === $firstOption['value']) {
diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php
index 8fd73e466083a..68b4d849099c1 100644
--- a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php
+++ b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php
@@ -113,6 +113,7 @@ protected function _prepareForm()
'class' => $behaviorCode,
'onchange' => 'varienImport.handleImportBehaviorSelector();',
'note' => ' ',
+ 'after_element_html' => $this->getImportBehaviorTooltip(),
]
);
$fieldsets[$behaviorCode]->addField(
@@ -252,4 +253,19 @@ protected function getDownloadSampleFileHtml()
. '';
return $html;
}
+
+ /**
+ * Get Import Behavior field tooltip
+ *
+ * @return string
+ */
+ private function getImportBehaviorTooltip()
+ {
+ $html = '';
+ return $html;
+ }
}
diff --git a/app/code/Magento/ImportExport/Model/Source/Import/Behavior/Basic.php b/app/code/Magento/ImportExport/Model/Source/Import/Behavior/Basic.php
index f28aae9f8ae1c..e0002474e1917 100644
--- a/app/code/Magento/ImportExport/Model/Source/Import/Behavior/Basic.php
+++ b/app/code/Magento/ImportExport/Model/Source/Import/Behavior/Basic.php
@@ -5,6 +5,8 @@
*/
namespace Magento\ImportExport\Model\Source\Import\Behavior;
+use Magento\ImportExport\Model\Import;
+
/**
* Import behavior source model used for defining the behaviour during the import.
*
@@ -14,19 +16,19 @@
class Basic extends \Magento\ImportExport\Model\Source\Import\AbstractBehavior
{
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function toArray()
{
return [
- \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND => __('Add/Update'),
- \Magento\ImportExport\Model\Import::BEHAVIOR_REPLACE => __('Replace'),
- \Magento\ImportExport\Model\Import::BEHAVIOR_DELETE => __('Delete')
+ Import::BEHAVIOR_APPEND => __('Add/Update'),
+ Import::BEHAVIOR_REPLACE => __('Replace'),
+ Import::BEHAVIOR_DELETE => __('Delete')
];
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getCode()
{
@@ -34,12 +36,23 @@ public function getCode()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getNotes($entityCode)
{
$messages = ['catalog_product' => [
- \Magento\ImportExport\Model\Import::BEHAVIOR_REPLACE => __("Note: Product IDs will be regenerated.")
+ Import::BEHAVIOR_APPEND => __(
+ "New product data is added to the existing product data for the existing entries in the database. "
+ . "All fields except sku can be updated."
+ ),
+ Import::BEHAVIOR_REPLACE => __(
+ "The existing product data is replaced with new data. Exercise caution when replacing data "
+ . "because the existing product data will be completely cleared and all references "
+ . "in the system will be lost. "
+ ),
+ Import::BEHAVIOR_DELETE => __(
+ "Any entities in the import data that already exist in the database are deleted from the database."
+ ),
]];
return isset($messages[$entityCode]) ? $messages[$entityCode] : [];
}
diff --git a/app/code/Magento/ImportExport/Test/Mftf/Page/AdminImportIndexPage.xml b/app/code/Magento/ImportExport/Test/Mftf/Page/AdminImportIndexPage.xml
new file mode 100644
index 0000000000000..87807eb9b0e85
--- /dev/null
+++ b/app/code/Magento/ImportExport/Test/Mftf/Page/AdminImportIndexPage.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportHeaderSection.xml b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportHeaderSection.xml
new file mode 100644
index 0000000000000..748580be09406
--- /dev/null
+++ b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportHeaderSection.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml
new file mode 100644
index 0000000000000..2ce6b1e35777f
--- /dev/null
+++ b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml
new file mode 100644
index 0000000000000..4cbb0603d9073
--- /dev/null
+++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Simple Product for Test
+ Simple Product for Test
+
+
+
+ Virtual Product for Test
+ Virtual Product for Test
+
+
+
+ Api Downloadable Product for Test
+ Api Downloadable Product for Test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Import/ErrorProcessing/ProcessingErrorAggregatorTest.php b/app/code/Magento/ImportExport/Test/Unit/Model/Import/ErrorProcessing/ProcessingErrorAggregatorTest.php
index a4c380d51ebe3..b81b9f9093d1f 100644
--- a/app/code/Magento/ImportExport/Test/Unit/Model/Import/ErrorProcessing/ProcessingErrorAggregatorTest.php
+++ b/app/code/Magento/ImportExport/Test/Unit/Model/Import/ErrorProcessing/ProcessingErrorAggregatorTest.php
@@ -191,7 +191,7 @@ public function testInitValidationStrategyExceed()
}
/**
- * Test for method initValidationStrategy. Expected exeption due null incoming parameter
+ * Test for method initValidationStrategy. Expected exception due null incoming parameter
*/
public function testInitValidationStrategyException()
{
@@ -316,7 +316,7 @@ public function testAddTheSameErrorTwice()
}
/**
- * Test for method getErrorsByCode. Expects receive errors with code, which present in incomming parameter.
+ * Test for method getErrorsByCode. Expects receive errors with code, which present in incoming parameter.
*/
public function testGetErrorsByCodeInArray()
{
@@ -328,7 +328,7 @@ public function testGetErrorsByCodeInArray()
}
/**
- * Test for method getErrorsByCode. Unexpects receive errors with code, which present in incomming parameter.
+ * Test for method getErrorsByCode. Unexpects receive errors with code, which present in incoming parameter.
*/
public function testGetErrorsByCodeNotInArray()
{
diff --git a/app/code/Magento/ImportExport/i18n/en_US.csv b/app/code/Magento/ImportExport/i18n/en_US.csv
index 60c9ee4c5a960..cae4d6e19868d 100644
--- a/app/code/Magento/ImportExport/i18n/en_US.csv
+++ b/app/code/Magento/ImportExport/i18n/en_US.csv
@@ -119,3 +119,6 @@ User,User
"Error File","Error File"
"Execution Time","Execution Time"
Summary,Summary
+"New product data is added to existing product data entries in the database. All fields except SKU can be updated.","New product data is added to existing product data entries in the database. All fields except SKU can be updated."
+"All existing product data is replaced with the imported new data. Exercise caution when replacing data. All existing product data will be completely cleared and all references in the system will be lost. ","All existing product data is replaced with the imported new data. Exercise caution when replacing data. All existing product data will be completely cleared and all references in the system will be lost. "
+"Any entities in the import data that match existing entities in the database are deleted from the database.","Any entities in the import data that match existing entities in the database are deleted from the database."
diff --git a/app/code/Magento/Indexer/Model/Message/Invalid.php b/app/code/Magento/Indexer/Model/Message/Invalid.php
index 5a3f879b0ad80..79f9fcef9641e 100644
--- a/app/code/Magento/Indexer/Model/Message/Invalid.php
+++ b/app/code/Magento/Indexer/Model/Message/Invalid.php
@@ -6,6 +6,9 @@
namespace Magento\Indexer\Model\Message;
+/**
+ * Message about invalid indexers.
+ */
class Invalid implements \Magento\Framework\Notification\MessageInterface
{
/**
@@ -71,7 +74,7 @@ public function getText()
return __(
'One or more indexers are invalid . Make sure your Magento cron job is running.',
$url,
- 'http://devdocs.magento.com/guides/v2.2/config-guide/cli/config-cli-subcommands-cron.html#create-or-remove-the-magento-crontab'
+ 'https://devdocs.magento.com/guides/v2.2/config-guide/cli/config-cli-subcommands-cron.html#create-or-remove-the-magento-crontab'
);
//@codingStandardsIgnoreEnd
}
diff --git a/app/code/Magento/InstantPurchase/PaymentMethodIntegration/IntegrationFactory.php b/app/code/Magento/InstantPurchase/PaymentMethodIntegration/IntegrationFactory.php
index cdca81ed98b7a..0b698afea1de3 100644
--- a/app/code/Magento/InstantPurchase/PaymentMethodIntegration/IntegrationFactory.php
+++ b/app/code/Magento/InstantPurchase/PaymentMethodIntegration/IntegrationFactory.php
@@ -65,13 +65,13 @@ public function create(VaultPaymentInterface $paymentMethod, int $storeId): Inte
/**
* Reads value from config.
*
- * @param $config
+ * @param array $config
* @param string $field
* @param string $default
* @return string
*/
private function extractFromConfig($config, string $field, string $default): string
{
- return isset($config[$field]) ? $config[$field] : $default;
+ return $config[$field] ?? $default;
}
}
diff --git a/app/code/Magento/InstantPurchase/README.md b/app/code/Magento/InstantPurchase/README.md
index 534696bf353fc..9b618eaca997d 100644
--- a/app/code/Magento/InstantPurchase/README.md
+++ b/app/code/Magento/InstantPurchase/README.md
@@ -10,7 +10,7 @@ Prerequisites to display the Instant Purchase button:
## Structure
-In addition to [a typical file structure for a Magento 2 module](http://devdocs.magento.com/guides/v2.2/extension-dev-guide/build/module-file-structure.html) `PaymentMethodsIntegration` directory contains interfaces and basic implementation of integration vault payment method to the instant purchase.
+In addition to [a typical file structure for a Magento 2 module](https://devdocs.magento.com/guides/v2.2/extension-dev-guide/build/module-file-structure.html) `PaymentMethodsIntegration` directory contains interfaces and basic implementation of integration vault payment method to the instant purchase.
## Extensibility
@@ -22,7 +22,7 @@ All payments created for instant purchase also have `'instant-purchase' => true`
### Payment method integration
-Instant purchase support may be implemented for any payment method with [vault support](http://devdocs.magento.com/guides/v2.1/payments-integrations/vault/vault-intro.html).
+Instant purchase support may be implemented for any payment method with [vault support](https://devdocs.magento.com/guides/v2.1/payments-integrations/vault/vault-intro.html).
Basic implementation provided in `Magento\InstantPurchase\PaymentMethodIntegration` should be enough in most cases. It is not enabled by default to avoid issues on production sites and authors of vault payment method should verify correct work for instant purchase manually.
To enable basic implementation just add single option to configuration of payemnt method in `config.xml`:
@@ -52,7 +52,7 @@ Basic implementation is a good start point but it's recommended to provide own i
The `Magento_InstantPurchase` module does not introduce backward incompatible changes.
-You can track [backward incompatible changes in patch releases](http://devdocs.magento.com/guides/v2.2/release-notes/changes/ce_changes.html).
+You can track [backward incompatible changes in patch releases](https://devdocs.magento.com/guides/v2.2/release-notes/changes/ce_changes.html).
***
diff --git a/app/code/Magento/Integration/view/adminhtml/web/js/integration.js b/app/code/Magento/Integration/view/adminhtml/web/js/integration.js
index 6921f645a2330..a2acc40c6b86e 100644
--- a/app/code/Magento/Integration/view/adminhtml/web/js/integration.js
+++ b/app/code/Magento/Integration/view/adminhtml/web/js/integration.js
@@ -187,7 +187,7 @@ define([
},
/**
- * Function to check the location of the child popoup window.
+ * Function to check the location of the child popup window.
* Once detected if the callback is successful, parent window will be reloaded
*/
fnCheckLocation: function () {
diff --git a/app/code/Magento/LayeredNavigation/etc/adminhtml/system.xml b/app/code/Magento/LayeredNavigation/etc/adminhtml/system.xml
index de4637847456e..8d3f70c2806aa 100644
--- a/app/code/Magento/LayeredNavigation/etc/adminhtml/system.xml
+++ b/app/code/Magento/LayeredNavigation/etc/adminhtml/system.xml
@@ -20,7 +20,7 @@
Default Price Navigation Step
- validate-number validate-number-range number-range-0.01-1000000000
+ validate-number validate-number-range number-range-0.01-9999999999999999
manual
diff --git a/app/code/Magento/Msrp/view/base/layout/cms_index_index.xml b/app/code/Magento/Msrp/view/base/layout/cms_index_index.xml
new file mode 100644
index 0000000000000..b60bc71e9e70d
--- /dev/null
+++ b/app/code/Magento/Msrp/view/base/layout/cms_index_index.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml b/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml
index 869d81563645a..dd5abd433073d 100644
--- a/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml
+++ b/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml
@@ -72,6 +72,8 @@ if ($product->isSaleable()) {
} else {
$data['addToCart']['addToCartButton'] = sprintf(
'form:has(input[type="hidden"][name="product"][value="%s"]) button[type="submit"]',
+ (int) $productId) . ',' .
+ sprintf('.block.widget .price-box[data-product-id=%s]+.product-item-actions button.tocart',
(int) $productId
);
}
diff --git a/app/code/Magento/Msrp/view/frontend/templates/popup.phtml b/app/code/Magento/Msrp/view/frontend/templates/popup.phtml
index 7ccd606b8f113..e0b3dd77dedcb 100644
--- a/app/code/Magento/Msrp/view/frontend/templates/popup.phtml
+++ b/app/code/Magento/Msrp/view/frontend/templates/popup.phtml
@@ -30,12 +30,11 @@