diff --git a/app/code/Magento/Backend/Block/Store/Switcher.php b/app/code/Magento/Backend/Block/Store/Switcher.php
index 2ae929f9b7398..76b6c54fdae50 100644
--- a/app/code/Magento/Backend/Block/Store/Switcher.php
+++ b/app/code/Magento/Backend/Block/Store/Switcher.php
@@ -7,6 +7,8 @@
namespace Magento\Backend\Block\Store;
+use Magento\Framework\Exception\LocalizedException;
+
/**
* Store switcher block
*
@@ -471,9 +473,17 @@ public function getCurrentSelectionName()
*/
public function getCurrentWebsiteName()
{
- if ($this->getWebsiteId() !== null) {
+ $websiteId = $this->getWebsiteId();
+ if ($websiteId !== null) {
+ if ($this->hasData('get_data_from_request')) {
+ $requestedWebsite = $this->getRequest()->getParams('website');
+ if (!empty($requestedWebsite)
+ && array_key_exists('website', $requestedWebsite)) {
+ $websiteId = $requestedWebsite['website'];
+ }
+ }
$website = $this->_websiteFactory->create();
- $website->load($this->getWebsiteId());
+ $website->load($websiteId);
if ($website->getId()) {
return $website->getName();
}
@@ -504,12 +514,21 @@ public function getCurrentStoreGroupName()
* Get current store view name
*
* @return string
+ * @throws LocalizedException
*/
public function getCurrentStoreName()
{
- if ($this->getStoreId() !== null) {
+ $storeId = $this->getStoreId();
+ if ($storeId !== null) {
+ if ($this->hasData('get_data_from_request')) {
+ $requestedStore = $this->getRequest()->getParams('store');
+ if (!empty($requestedStore)
+ && array_key_exists('store', $requestedStore)) {
+ $storeId = $requestedStore['store'];
+ }
+ }
$store = $this->_storeFactory->create();
- $store->load($this->getStoreId());
+ $store->load($storeId);
if ($store->getId()) {
return $store->getName();
}
diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminOrderViewPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminOrderViewPage.xml
new file mode 100644
index 0000000000000..cfdb99ee36251
--- /dev/null
+++ b/app/code/Magento/Backend/Test/Mftf/Page/AdminOrderViewPage.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
diff --git a/app/code/Magento/Backend/Test/Unit/Block/Store/SwitcherTest.php b/app/code/Magento/Backend/Test/Unit/Block/Store/SwitcherTest.php
index 42b7254d8f3da..85f4709994e2a 100644
--- a/app/code/Magento/Backend/Test/Unit/Block/Store/SwitcherTest.php
+++ b/app/code/Magento/Backend/Test/Unit/Block/Store/SwitcherTest.php
@@ -8,10 +8,15 @@
namespace Magento\Backend\Test\Unit\Block\Store;
use Magento\Backend\Block\Store\Switcher;
+use Magento\Framework\App\RequestInterface;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Store\Model\StoreFactory;
+use Magento\Store\Model\Store;
+use Magento\Store\Model\WebsiteFactory;
+use Magento\Store\Model\Website;
use Magento\Backend\Block\Template\Context;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
use Magento\Store\Model\StoreManagerInterface;
-use Magento\Store\Model\Website;
use PHPUnit\Framework\TestCase;
class SwitcherTest extends TestCase
@@ -23,20 +28,81 @@ class SwitcherTest extends TestCase
private $storeManagerMock;
+ /**
+ * @var RequestInterface|MockObject
+ */
+ private $requestMock;
+
+ /**
+ * @var WebsiteFactory|MockObject
+ */
+ private $websiteFactoryMock;
+
+ /**
+ * @var StoreFactory|MockObject
+ */
+ private $storeFactoryMock;
+
+ /**
+ * @var Website|MockObject
+ */
+ private $websiteMock;
+
+ /**
+ * @var Store|MockObject
+ */
+ private $storeMock;
+
protected function setUp(): void
{
$this->storeManagerMock = $this->getMockForAbstractClass(StoreManagerInterface::class);
$objectHelper = new ObjectManager($this);
+ $this->requestMock = $this->getMockBuilder(RequestInterface::class)
+ ->getMockForAbstractClass();
+ $this->websiteFactoryMock = $this->getMockBuilder(WebsiteFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create'])
+ ->getMock();
+ $this->storeFactoryMock = $this->getMockBuilder(StoreFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create'])
+ ->getMock();
+ $this->websiteMock = $this->getMockBuilder(Website::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['load', 'getId', 'getName'])
+ ->getMock();
+ $this->storeMock = $this->getMockBuilder(Store::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['load', 'getId', 'getName'])
+ ->getMock();
+ $this->websiteFactoryMock->expects($this->any())
+ ->method('create')
+ ->willReturn($this->websiteMock);
+ $this->storeFactoryMock->expects($this->any())
+ ->method('create')
+ ->willReturn($this->storeMock);
+ $this->websiteMock->expects($this->any())
+ ->method('load')
+ ->willReturnSelf();
+ $this->storeMock->expects($this->any())
+ ->method('load')
+ ->willReturnSelf();
$context = $objectHelper->getObject(
Context::class,
[
'storeManager' => $this->storeManagerMock,
+ 'request' => $this->requestMock
]
);
$this->switcherBlock = $objectHelper->getObject(
Switcher::class,
- ['context' => $context]
+ [
+ 'context' => $context,
+ 'data' => ['get_data_from_request' => 1],
+ 'websiteFactory' => $this->websiteFactoryMock,
+ 'storeFactory' => $this->storeFactoryMock
+ ]
);
}
@@ -58,4 +124,91 @@ public function testGetWebsitesIfSetWebsiteIds()
$expected = [1 => $websiteMock];
$this->assertEquals($expected, $this->switcherBlock->getWebsites());
}
+
+ /**
+ * Test case for after current store name plugin
+ *
+ * @param array $requestedStore
+ * @param string $expectedResult
+ * @return void
+ * @dataProvider getStoreNameDataProvider
+ * @throws LocalizedException
+ */
+ public function testAfterGetCurrentStoreName(array $requestedStore, string $expectedResult): void
+ {
+ $this->requestMock->expects($this->any())
+ ->method('getParams')
+ ->willReturn($requestedStore);
+ $this->storeMock->expects($this->any())
+ ->method('getId')
+ ->willReturn($requestedStore);
+ $this->storeMock->expects($this->any())
+ ->method('getName')
+ ->willReturn($expectedResult);
+ $this->assertSame($expectedResult, $this->switcherBlock->getCurrentStoreName());
+ }
+
+ /**
+ * Data provider for getStoreName plugin
+ *
+ * @return array
+ */
+ public function getStoreNameDataProvider(): array
+ {
+ return [
+ 'test storeName with valid requested store' =>
+ [
+ ['store' => 'test store'],
+ 'base store'
+ ],
+ 'test storeName with invalid requested store' =>
+ [
+ ['store' => 'test store'],
+ 'test store'
+ ]
+ ];
+ }
+
+ /**
+ * Test case for get current website name
+ *
+ * @param array $requestedWebsite
+ * @param string $expectedResult
+ * @return void
+ * @dataProvider getWebsiteNameDataProvider
+ */
+ public function testGetCurrentWebsiteName(array $requestedWebsite, string $expectedResult): void
+ {
+ $this->requestMock->expects($this->any())
+ ->method('getParams')
+ ->willReturn($requestedWebsite);
+ $this->websiteMock->expects($this->any())
+ ->method('getId')
+ ->willReturn($requestedWebsite);
+ $this->websiteMock->expects($this->any())
+ ->method('getName')
+ ->willReturn($expectedResult);
+ $this->assertSame($expectedResult, $this->switcherBlock->getCurrentWebsiteName());
+ }
+
+ /**
+ * Data provider for getWebsiteName plugin
+ *
+ * @return array
+ */
+ public function getWebsiteNameDataProvider(): array
+ {
+ return [
+ 'test websiteName with valid requested website' =>
+ [
+ ['website' => 'test website'],
+ 'base website'
+ ],
+ 'test websiteName with invalid requested website' =>
+ [
+ ['website' => 'test website'],
+ 'test website'
+ ]
+ ];
+ }
}
diff --git a/app/code/Magento/Catalog/Model/Product/Copier.php b/app/code/Magento/Catalog/Model/Product/Copier.php
index b04d3da8f0223..0ce40d0523c73 100644
--- a/app/code/Magento/Catalog/Model/Product/Copier.php
+++ b/app/code/Magento/Catalog/Model/Product/Copier.php
@@ -6,12 +6,13 @@
namespace Magento\Catalog\Model\Product;
use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Model\Attribute\ScopeOverriddenValue;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\Product\Attribute\Source\Status;
use Magento\Catalog\Model\Product\Option\Repository as OptionRepository;
use Magento\Catalog\Model\ProductFactory;
-use Magento\Framework\App\ObjectManager;
+use Magento\Catalog\Model\ResourceModel\DuplicatedProductAttributesCopier;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Store\Model\Store;
use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException;
@@ -50,25 +51,41 @@ class Copier
*/
private $scopeOverriddenValue;
+ /**
+ * @var ProductRepositoryInterface
+ */
+ private $productRepository;
+
+ /**
+ * @var DuplicatedProductAttributesCopier
+ */
+ private $attributeCopier;
+
/**
* @param CopyConstructorInterface $copyConstructor
* @param ProductFactory $productFactory
* @param ScopeOverriddenValue $scopeOverriddenValue
* @param OptionRepository|null $optionRepository
* @param MetadataPool|null $metadataPool
+ * @param ProductRepositoryInterface $productRepository
+ * @param DuplicatedProductAttributesCopier $attributeCopier
*/
public function __construct(
CopyConstructorInterface $copyConstructor,
ProductFactory $productFactory,
ScopeOverriddenValue $scopeOverriddenValue,
OptionRepository $optionRepository,
- MetadataPool $metadataPool
+ MetadataPool $metadataPool,
+ ProductRepositoryInterface $productRepository,
+ DuplicatedProductAttributesCopier $attributeCopier
) {
$this->productFactory = $productFactory;
$this->copyConstructor = $copyConstructor;
$this->scopeOverriddenValue = $scopeOverriddenValue;
$this->optionRepository = $optionRepository;
$this->metadataPool = $metadataPool;
+ $this->productRepository = $productRepository;
+ $this->attributeCopier = $attributeCopier;
}
/**
@@ -79,11 +96,13 @@ public function __construct(
*/
public function copy(Product $product): Product
{
- $product->getWebsiteIds();
- $product->getCategoryIds();
-
$metadata = $this->metadataPool->getMetadata(ProductInterface::class);
+ /* Regardless in what scope the product was provided,
+ for duplicating we want to clone product in Global scope first */
+ if ((int)$product->getStoreId() !== Store::DEFAULT_STORE_ID) {
+ $product = $this->productRepository->getById($product->getId(), true, Store::DEFAULT_STORE_ID);
+ }
/** @var Product $duplicate */
$duplicate = $this->productFactory->create();
$productData = $product->getData();
@@ -102,6 +121,7 @@ public function copy(Product $product): Product
$duplicate->setStoreId(Store::DEFAULT_STORE_ID);
$this->copyConstructor->build($product, $duplicate);
$this->setDefaultUrl($product, $duplicate);
+ $this->attributeCopier->copyProductAttributes($product, $duplicate);
$this->setStoresUrl($product, $duplicate);
$this->optionRepository->duplicate($product, $duplicate);
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/DuplicatedProductAttributesCopier.php b/app/code/Magento/Catalog/Model/ResourceModel/DuplicatedProductAttributesCopier.php
new file mode 100644
index 0000000000000..5c46b379cda32
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/ResourceModel/DuplicatedProductAttributesCopier.php
@@ -0,0 +1,99 @@
+metadataPool = $metadataPool;
+ $this->collectionFactory = $collectionFactory;
+ $this->resource = $resource;
+ }
+
+ /**
+ * Copy non-global Product Attributes form source to target
+ *
+ * @param $source Product
+ * @param $target Product
+ * @return void
+ */
+ public function copyProductAttributes(Product $source, Product $target): void
+ {
+ $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
+ $linkField = $metadata->getLinkField();
+ $attributeCollection = $this->collectionFactory->create()
+ ->setAttributeSetFilter($source->getAttributeSetId())
+ ->addFieldToFilter('backend_type', ['neq' => 'static'])
+ ->addFieldToFilter('is_global', 0);
+
+ $eavTableNames = [];
+ foreach ($attributeCollection->getItems() as $item) {
+ /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $item */
+ $eavTableNames[] = $item->getBackendTable();
+ }
+
+ $connection = $this->resource->getConnection();
+ foreach (array_unique($eavTableNames) as $eavTable) {
+ $select = $connection->select()
+ ->from(
+ ['main_table' => $this->resource->getTableName($eavTable)],
+ ['attribute_id', 'store_id', 'value']
+ )->where($linkField . ' = ?', $source->getData($linkField))
+ ->where('store_id <> ?', Store::DEFAULT_STORE_ID);
+ $records = $connection->fetchAll($select);
+
+ if (!count($records)) {
+ continue;
+ }
+
+ foreach ($records as $index => $bind) {
+ $bind[$linkField] = $target->getData($linkField);
+ $records[$index] = $bind;
+ }
+
+ $connection->insertMultiple($this->resource->getTableName($eavTable), $records);
+ }
+ }
+}
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
index a247e6b09760b..246192fdc7bd5 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
@@ -1767,9 +1767,7 @@ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC)
if ($attribute == 'price' && $storeId != 0) {
$this->addPriceData();
if ($this->_productLimitationFilters->isUsingPriceIndex()) {
- $this->getSelect()->order(
- new \Zend_Db_Expr("price_index.min_price = 0, price_index.min_price {$dir}")
- );
+ $this->getSelect()->order("price_index.min_price {$dir}");
return $this;
}
}
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php
index be1d1637b8532..f78dbcb14c3f4 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php
@@ -305,7 +305,7 @@ private function getTotalTierPriceExpression(\Zend_Db_Expr $priceExpression)
private function getTierPriceExpressionForTable($tableAlias, \Zend_Db_Expr $priceExpression): \Zend_Db_Expr
{
return $this->getConnection()->getCheckSql(
- sprintf('%s.value = 0', $tableAlias),
+ sprintf('%s.percentage_value IS NOT NULL', $tableAlias),
sprintf(
'ROUND(%s * (1 - ROUND(%s.percentage_value * cwd.rate, 4) / 100), 4)',
$priceExpression,
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/CopierTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/CopierTest.php
deleted file mode 100644
index d3a4494c071b7..0000000000000
--- a/app/code/Magento/Catalog/Test/Unit/Model/Product/CopierTest.php
+++ /dev/null
@@ -1,394 +0,0 @@
-copyConstructorMock = $this->getMockForAbstractClass(CopyConstructorInterface::class);
- $this->productFactoryMock = $this->createPartialMock(ProductFactory::class, ['create']);
- $this->scopeOverriddenValueMock = $this->createMock(ScopeOverriddenValue::class);
- $this->optionRepositoryMock = $this->createMock(Repository::class);
- $this->productMock = $this->createMock(Product::class);
-
- $this->metadata = $this->getMockBuilder(EntityMetadata::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- /** @var MetadataPool|MockObject $metadataPool */
- $metadataPool = $this->getMockBuilder(MetadataPool::class)
- ->disableOriginalConstructor()
- ->getMock();
- $metadataPool->expects($this->once())
- ->method('getMetadata')
- ->willReturn($this->metadata);
- $this->_model = new Copier(
- $this->copyConstructorMock,
- $this->productFactoryMock,
- $this->scopeOverriddenValueMock,
- $this->optionRepositoryMock,
- $metadataPool
- );
- }
-
- /**
- * Test duplicate product
- *
- * @return void
- * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
- */
- public function testCopy(): void
- {
- $stockItem = $this->getMockForAbstractClass(StockItemInterface::class);
- $extensionAttributes = $this->getMockBuilder(ProductExtensionInterface::class)
- ->setMethods(['getStockItem', 'setData'])
- ->getMockForAbstractClass();
- $extensionAttributes
- ->expects($this->once())
- ->method('getStockItem')
- ->willReturn($stockItem);
- $extensionAttributes
- ->expects($this->once())
- ->method('setData')
- ->with('stock_item', null);
-
- $productData = [
- 'product data' => ['product data'],
- ProductInterface::EXTENSION_ATTRIBUTES_KEY => $extensionAttributes,
- ];
- $this->productMock->expects($this->atLeastOnce())
- ->method('getWebsiteIds');
- $this->productMock->expects($this->atLeastOnce())
- ->method('getCategoryIds');
- $this->productMock->expects($this->exactly(2))
- ->method('getData')
- ->willReturnMap([
- ['', null, $productData],
- ['linkField', null, '1'],
- ]);
-
- $entityMock = $this->getMockForAbstractClass(
- AbstractEntity::class,
- [],
- '',
- false,
- true,
- true,
- ['checkAttributeUniqueValue']
- );
- $entityMock->expects($this->once())
- ->method('checkAttributeUniqueValue')
- ->willReturn(true);
-
- $attributeMock = $this->getMockForAbstractClass(
- AbstractAttribute::class,
- [],
- '',
- false,
- true,
- true,
- ['getEntity']
- );
- $attributeMock->expects($this->once())
- ->method('getEntity')
- ->willReturn($entityMock);
-
- $resourceMock = $this->getMockBuilder(ProductResourceModel::class)
- ->disableOriginalConstructor()
- ->setMethods(['getAttributeRawValue', 'duplicate', 'getAttribute'])
- ->getMock();
- $resourceMock->expects($this->once())
- ->method('getAttributeRawValue')
- ->willReturn('urk-key-1');
- $resourceMock->expects($this->exactly(2))
- ->method('getAttribute')
- ->willReturn($attributeMock);
-
- $this->productMock->expects($this->exactly(2))
- ->method('getResource')
- ->willReturn($resourceMock);
-
- $duplicateMock = $this->getMockBuilder(Product::class)
- ->addMethods(
- [
- 'setIsDuplicate',
- 'setOriginalLinkId',
- 'setUrlKey',
- 'setMetaTitle',
- 'setMetaKeyword',
- 'setMetaDescription'
- ]
- )
- ->onlyMethods(
- [
- 'setData',
- 'setOptions',
- 'getData',
- 'setStatus',
- 'setCreatedAt',
- 'setUpdatedAt',
- 'setId',
- 'getEntityId',
- 'save',
- 'setStoreId',
- 'getStoreIds'
- ]
- )
- ->disableOriginalConstructor()
- ->getMock();
- $this->productFactoryMock->expects($this->once())
- ->method('create')
- ->willReturn($duplicateMock);
-
- $duplicateMock->expects($this->once())->method('setOptions')->with([]);
- $duplicateMock->expects($this->once())->method('setIsDuplicate')->with(true);
- $duplicateMock->expects($this->once())->method('setOriginalLinkId')->with(1);
- $duplicateMock->expects($this->once())
- ->method('setStatus')
- ->with(Status::STATUS_DISABLED);
- $duplicateMock->expects($this->atLeastOnce())->method('setStoreId');
- $duplicateMock->expects($this->once())
- ->method('setCreatedAt')
- ->with(null);
- $duplicateMock->expects($this->once())
- ->method('setUpdatedAt')
- ->with(null);
- $duplicateMock->expects($this->once())
- ->method('setId')
- ->with(null);
- $duplicateMock->expects($this->once())
- ->method('setMetaTitle')
- ->with(null);
- $duplicateMock->expects($this->once())
- ->method('setMetaKeyword')
- ->with(null);
- $duplicateMock->expects($this->once())
- ->method('setMetaDescription')
- ->with(null);
- $duplicateMock->expects($this->atLeastOnce())
- ->method('getStoreIds')->willReturn([]);
- $duplicateMock->expects($this->atLeastOnce())
- ->method('setData')
- ->willReturn($duplicateMock);
- $this->copyConstructorMock->expects($this->once())
- ->method('build')
- ->with($this->productMock, $duplicateMock);
- $duplicateMock->expects($this->once())
- ->method('setUrlKey')
- ->with('urk-key-2')
- ->willReturn($duplicateMock);
- $duplicateMock->expects($this->once())
- ->method('save');
- $this->metadata->expects($this->once())
- ->method('getLinkField')
- ->willReturn('linkField');
- $duplicateMock->expects($this->never())
- ->method('getData');
- $this->optionRepositoryMock->expects($this->once())
- ->method('duplicate')
- ->with($this->productMock, $duplicateMock);
-
- $this->assertEquals($duplicateMock, $this->_model->copy($this->productMock));
- }
-
- /**
- * Test duplicate product with `UrlAlreadyExistsException` while copy stores url
- *
- * @return void
- * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
- */
- public function testUrlAlreadyExistsExceptionWhileCopyStoresUrl(): void
- {
- $stockItem = $this->getMockBuilder(StockItemInterface::class)
- ->getMock();
- $extensionAttributes = $this->getMockBuilder(ProductExtensionInterface::class)
- ->setMethods(['getStockItem', 'setData'])
- ->getMockForAbstractClass();
- $extensionAttributes
- ->expects($this->once())
- ->method('getStockItem')
- ->willReturn($stockItem);
- $extensionAttributes
- ->expects($this->once())
- ->method('setData')
- ->with('stock_item', null);
-
- $productData = [
- 'product data' => ['product data'],
- ProductInterface::EXTENSION_ATTRIBUTES_KEY => $extensionAttributes,
- ];
- $this->productMock->expects($this->atLeastOnce())->method('getWebsiteIds');
- $this->productMock->expects($this->atLeastOnce())->method('getCategoryIds');
- $this->productMock->expects($this->any())->method('getData')->willReturnMap([
- ['', null, $productData],
- ['linkField', null, '1'],
- ]);
-
- $entityMock = $this->getMockForAbstractClass(
- AbstractEntity::class,
- [],
- '',
- false,
- true,
- true,
- ['checkAttributeUniqueValue']
- );
- $entityMock->expects($this->exactly(11))
- ->method('checkAttributeUniqueValue')
- ->willReturn(true, false);
-
- $attributeMock = $this->getMockForAbstractClass(
- AbstractAttribute::class,
- [],
- '',
- false,
- true,
- true,
- ['getEntity']
- );
- $attributeMock->expects($this->any())
- ->method('getEntity')
- ->willReturn($entityMock);
-
- $resourceMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product::class)
- ->disableOriginalConstructor()
- ->setMethods(['getAttributeRawValue', 'duplicate', 'getAttribute'])
- ->getMock();
- $resourceMock->expects($this->any())
- ->method('getAttributeRawValue')
- ->willReturn('urk-key-1');
- $resourceMock->expects($this->any())
- ->method('getAttribute')
- ->willReturn($attributeMock);
-
- $this->productMock->expects($this->any())->method('getResource')->willReturn($resourceMock);
-
- $duplicateMock = $this->getMockBuilder(Product::class)
- ->addMethods(['setIsDuplicate', 'setOriginalLinkId', 'setUrlKey'])
- ->onlyMethods(
- [
- 'setData',
- 'setOptions',
- 'getData',
- 'setStatus',
- 'setCreatedAt',
- 'setUpdatedAt',
- 'setId',
- 'getEntityId',
- 'save',
- 'setStoreId',
- 'getStoreIds'
- ]
- )
- ->disableOriginalConstructor()
- ->getMock();
- $this->productFactoryMock->expects($this->once())->method('create')->willReturn($duplicateMock);
-
- $duplicateMock->expects($this->once())->method('setOptions')->with([]);
- $duplicateMock->expects($this->once())->method('setIsDuplicate')->with(true);
- $duplicateMock->expects($this->once())->method('setOriginalLinkId')->with(1);
- $duplicateMock->expects(
- $this->once()
- )->method(
- 'setStatus'
- )->with(
- Status::STATUS_DISABLED
- );
- $duplicateMock->expects($this->atLeastOnce())->method('setStoreId');
- $duplicateMock->expects($this->once())->method('setCreatedAt')->with(null);
- $duplicateMock->expects($this->once())->method('setUpdatedAt')->with(null);
- $duplicateMock->expects($this->once())->method('setId')->with(null);
- $duplicateMock->expects($this->atLeastOnce())->method('getStoreIds')->willReturn([1]);
- $duplicateMock->expects($this->atLeastOnce())->method('setData')->willReturn($duplicateMock);
- $this->copyConstructorMock->expects($this->once())->method('build')->with($this->productMock, $duplicateMock);
- $duplicateMock->expects(
- $this->exactly(11)
- )->method(
- 'setUrlKey'
- )->with(
- $this->stringContains('urk-key-')
- )->willReturn(
- $duplicateMock
- );
- $duplicateMock->expects($this->once())->method('save');
-
- $this->scopeOverriddenValueMock->expects($this->once())->method('containsValue')->willReturn(true);
-
- $this->metadata->expects($this->any())->method('getLinkField')->willReturn('linkField');
-
- $duplicateMock->expects($this->any())->method('getData')->willReturnMap([
- ['linkField', null, '2'],
- ]);
-
- $this->expectException(UrlAlreadyExistsException::class);
- $this->_model->copy($this->productMock);
- }
-}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php
index 13bd29e83d87f..dd82bf277a33a 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ProductSearch.php
@@ -8,6 +8,7 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider;
use Magento\Catalog\Api\Data\ProductSearchResultsInterfaceFactory;
+use Magento\Catalog\Model\Product\Visibility;
use Magento\Catalog\Model\ResourceModel\Product\Collection;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionPostProcessor;
@@ -18,6 +19,7 @@
use Magento\Framework\Api\Search\SearchResultInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SearchResultsInterface;
+use Magento\Framework\App\ObjectManager;
use Magento\GraphQl\Model\Query\ContextInterface;
/**
@@ -55,6 +57,11 @@ class ProductSearch
*/
private $searchCriteriaBuilder;
+ /**
+ * @var Visibility
+ */
+ private $catalogProductVisibility;
+
/**
* @param CollectionFactory $collectionFactory
* @param ProductSearchResultsInterfaceFactory $searchResultsFactory
@@ -62,6 +69,7 @@ class ProductSearch
* @param CollectionPostProcessor $collectionPostProcessor
* @param SearchResultApplierFactory $searchResultsApplierFactory
* @param ProductCollectionSearchCriteriaBuilder $searchCriteriaBuilder
+ * @param Visibility $catalogProductVisibility
*/
public function __construct(
CollectionFactory $collectionFactory,
@@ -69,7 +77,8 @@ public function __construct(
CollectionProcessorInterface $collectionPreProcessor,
CollectionPostProcessor $collectionPostProcessor,
SearchResultApplierFactory $searchResultsApplierFactory,
- ProductCollectionSearchCriteriaBuilder $searchCriteriaBuilder
+ ProductCollectionSearchCriteriaBuilder $searchCriteriaBuilder,
+ Visibility $catalogProductVisibility
) {
$this->collectionFactory = $collectionFactory;
$this->searchResultsFactory = $searchResultsFactory;
@@ -77,6 +86,7 @@ public function __construct(
$this->collectionPostProcessor = $collectionPostProcessor;
$this->searchResultApplierFactory = $searchResultsApplierFactory;
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
+ $this->catalogProductVisibility = $catalogProductVisibility;
}
/**
@@ -106,6 +116,7 @@ public function getList(
$this->getSortOrderArray($searchCriteriaForCollection)
)->apply();
+ $collection->setVisibility($this->catalogProductVisibility->getVisibleInSiteIds());
$this->collectionPreProcessor->process($collection, $searchCriteriaForCollection, $attributes, $context);
$collection->load();
$this->collectionPostProcessor->process($collection, $attributes);
diff --git a/app/code/Magento/CatalogRule/Cron/DailyCatalogUpdate.php b/app/code/Magento/CatalogRule/Cron/DailyCatalogUpdate.php
index 116a4529a8e60..f659df2ee0ecd 100644
--- a/app/code/Magento/CatalogRule/Cron/DailyCatalogUpdate.php
+++ b/app/code/Magento/CatalogRule/Cron/DailyCatalogUpdate.php
@@ -6,8 +6,8 @@
namespace Magento\CatalogRule\Cron;
-use Magento\CatalogRule\Model\Indexer\PartialIndex;
use Magento\CatalogRule\Model\Indexer\Rule\RuleProductProcessor;
+use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory as RuleCollectionFactory;
/**
* Daily update catalog price rule by cron
@@ -20,24 +20,25 @@ class DailyCatalogUpdate
protected $ruleProductProcessor;
/**
- * @var PartialIndex
+ * @var RuleCollectionFactory
*/
- private $partialIndex;
+ private $ruleCollectionFactory;
/**
* @param RuleProductProcessor $ruleProductProcessor
- * @param PartialIndex $partialIndex
+ * @param RuleCollectionFactory $ruleCollectionFactory
*/
public function __construct(
RuleProductProcessor $ruleProductProcessor,
- PartialIndex $partialIndex
+ RuleCollectionFactory $ruleCollectionFactory
) {
$this->ruleProductProcessor = $ruleProductProcessor;
- $this->partialIndex = $partialIndex;
+ $this->ruleCollectionFactory = $ruleCollectionFactory;
}
/**
* Daily update catalog price rule by cron
+ *
* Update include interval 3 days - current day - 1 days before + 1 days after
* This method is called from cron process, cron is working in UTC time and
* we should generate data for interval -1 day ... +1 day
@@ -46,8 +47,10 @@ public function __construct(
*/
public function execute()
{
- $this->ruleProductProcessor->isIndexerScheduled()
- ? $this->partialIndex->partialUpdateCatalogRuleProductPrice()
- : $this->ruleProductProcessor->markIndexerAsInvalid();
+ $ruleCollection = $this->ruleCollectionFactory->create();
+ $ruleCollection->addIsActiveFilter();
+ if ($ruleCollection->getSize()) {
+ $this->ruleProductProcessor->markIndexerAsInvalid();
+ }
}
}
diff --git a/app/code/Magento/CatalogRule/Model/Indexer/PartialIndex.php b/app/code/Magento/CatalogRule/Model/Indexer/PartialIndex.php
deleted file mode 100644
index 12a77f81826d6..0000000000000
--- a/app/code/Magento/CatalogRule/Model/Indexer/PartialIndex.php
+++ /dev/null
@@ -1,97 +0,0 @@
-resource = $resource;
- $this->connection = $resource->getConnection();
- $this->indexBuilder = $indexBuilder;
- }
-
- /**
- * Synchronization replica table with original table "catalogrule_product_price"
- *
- * Used replica table for correctly working MySQL trigger
- *
- * @return void
- */
- public function partialUpdateCatalogRuleProductPrice(): void
- {
- $this->indexBuilder->reindexFull();
- $indexTableName = $this->resource->getTableName('catalogrule_product_price');
- $select = $this->connection->select()->from(
- ['crp' => $indexTableName],
- 'product_id'
- );
- $selectFields = $this->connection->select()->from(
- ['crp' => $indexTableName],
- [
- 'rule_date',
- 'customer_group_id',
- 'product_id',
- 'rule_price',
- 'website_id',
- 'latest_start_date',
- 'earliest_end_date',
- ]
- );
- $where = ['product_id' .' NOT IN (?)' => $select];
- //remove products that are no longer used in indexing
- $this->connection->delete($this->resource->getTableName('catalogrule_product_price_replica'), $where);
- //add updated products to indexing
- $this->connection->query(
- $this->connection->insertFromSelect(
- $selectFields,
- $this->resource->getTableName('catalogrule_product_price_replica'),
- [
- 'rule_date',
- 'customer_group_id',
- 'product_id',
- 'rule_price',
- 'website_id',
- 'latest_start_date',
- 'earliest_end_date',
- ],
- AdapterInterface::INSERT_ON_DUPLICATE
- )
- );
- }
-}
diff --git a/app/code/Magento/CatalogRule/Test/Unit/Cron/DailyCatalogUpdateTest.php b/app/code/Magento/CatalogRule/Test/Unit/Cron/DailyCatalogUpdateTest.php
index c3e596ca4961d..e1dd10f921155 100644
--- a/app/code/Magento/CatalogRule/Test/Unit/Cron/DailyCatalogUpdateTest.php
+++ b/app/code/Magento/CatalogRule/Test/Unit/Cron/DailyCatalogUpdateTest.php
@@ -5,49 +5,71 @@
*/
declare(strict_types=1);
-
namespace Magento\CatalogRule\Test\Unit\Cron;
use Magento\CatalogRule\Cron\DailyCatalogUpdate;
use Magento\CatalogRule\Model\Indexer\Rule\RuleProductProcessor;
-use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\CatalogRule\Model\ResourceModel\Rule\Collection as RuleCollection;
+use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory as RuleCollectionFactory;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class DailyCatalogUpdateTest extends TestCase
{
/**
- * Processor
- *
* @var RuleProductProcessor|MockObject
*/
- protected $ruleProductProcessor;
+ private $ruleProductProcessor;
+
+ /**
+ * @var RuleCollectionFactory|MockObject
+ */
+ private $ruleCollectionFactory;
/**
- * Cron object
- *
* @var DailyCatalogUpdate
*/
- protected $cron;
+ private $cron;
protected function setUp(): void
{
- $this->ruleProductProcessor = $this->createMock(
- RuleProductProcessor::class
- );
-
- $this->cron = (new ObjectManager($this))->getObject(
- DailyCatalogUpdate::class,
- [
- 'ruleProductProcessor' => $this->ruleProductProcessor,
- ]
- );
+ $this->ruleProductProcessor = $this->createMock(RuleProductProcessor::class);
+ $this->ruleCollectionFactory = $this->createMock(RuleCollectionFactory::class);
+
+ $this->cron = new DailyCatalogUpdate($this->ruleProductProcessor, $this->ruleCollectionFactory);
}
- public function testDailyCatalogUpdate()
+ /**
+ * @dataProvider executeDataProvider
+ * @param int $activeRulesCount
+ * @param bool $isInvalidationNeeded
+ */
+ public function testExecute(int $activeRulesCount, bool $isInvalidationNeeded)
{
- $this->ruleProductProcessor->expects($this->once())->method('markIndexerAsInvalid');
+ $ruleCollection = $this->createMock(RuleCollection::class);
+ $this->ruleCollectionFactory->expects($this->once())
+ ->method('create')
+ ->willReturn($ruleCollection);
+ $ruleCollection->expects($this->once())
+ ->method('addIsActiveFilter')
+ ->willReturn($ruleCollection);
+ $ruleCollection->expects($this->once())
+ ->method('getSize')
+ ->willReturn($activeRulesCount);
+ $this->ruleProductProcessor->expects($isInvalidationNeeded ? $this->once() : $this->never())
+ ->method('markIndexerAsInvalid');
$this->cron->execute();
}
+
+ /**
+ * @return array
+ */
+ public function executeDataProvider(): array
+ {
+ return [
+ [2, true],
+ [0, false],
+ ];
+ }
}
diff --git a/app/code/Magento/CatalogRule/etc/mview.xml b/app/code/Magento/CatalogRule/etc/mview.xml
index 106e0ffabb2b2..daba451c79374 100644
--- a/app/code/Magento/CatalogRule/etc/mview.xml
+++ b/app/code/Magento/CatalogRule/etc/mview.xml
@@ -27,7 +27,6 @@
-
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithSignInLinkForEmailVerificationTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithSignInLinkForEmailVerificationTest.xml
new file mode 100644
index 0000000000000..2e1c8d5a27886
--- /dev/null
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithSignInLinkForEmailVerificationTest.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 560
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js
index 9c00050d886e8..ad44c00763dcd 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js
@@ -45,13 +45,6 @@ define([
resolveEstimationAddress: function () {
var address;
- if (checkoutData.getShippingAddressFromData()) {
- address = addressConverter.formAddressDataToQuoteAddress(checkoutData.getShippingAddressFromData());
- selectShippingAddress(address);
- } else {
- this.resolveShippingAddress();
- }
-
if (quote.isVirtual()) {
if (checkoutData.getBillingAddressFromData()) {
address = addressConverter.formAddressDataToQuoteAddress(
@@ -61,6 +54,11 @@ define([
} else {
this.resolveBillingAddress();
}
+ } else if (checkoutData.getShippingAddressFromData()) {
+ address = addressConverter.formAddressDataToQuoteAddress(checkoutData.getShippingAddressFromData());
+ selectShippingAddress(address);
+ } else {
+ this.resolveShippingAddress();
}
},
diff --git a/app/code/Magento/Checkout/view/frontend/web/template/authentication.html b/app/code/Magento/Checkout/view/frontend/web/template/authentication.html
index 4afaf3c89a5e0..97930a26a2f99 100644
--- a/app/code/Magento/Checkout/view/frontend/web/template/authentication.html
+++ b/app/code/Magento/Checkout/view/frontend/web/template/authentication.html
@@ -42,18 +42,20 @@