From a97cc6d01f6a015f53a00e642ffbe2463ac47ee0 Mon Sep 17 00:00:00 2001 From: Scott Buchanan Date: Wed, 15 Nov 2017 18:16:23 -0500 Subject: [PATCH 01/85] prevent layout cache corruption --- lib/internal/Magento/Framework/View/Model/Layout/Merge.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/internal/Magento/Framework/View/Model/Layout/Merge.php b/lib/internal/Magento/Framework/View/Model/Layout/Merge.php index 4d611c830e91a..06b00afe42ad9 100644 --- a/lib/internal/Magento/Framework/View/Model/Layout/Merge.php +++ b/lib/internal/Magento/Framework/View/Model/Layout/Merge.php @@ -431,6 +431,9 @@ public function load($handles = []) if ($result) { $this->addUpdate($result); $this->pageLayout = $this->_loadCache($cacheIdPageLayout); + foreach ($this->getHandles() as $handle) { + $this->allHandles[$handle] = $this->handleProcessed; + } return $this; } From ae09d7dad9a1c7774f9107aa143db0d9f21a9c03 Mon Sep 17 00:00:00 2001 From: Pavel Usachev Date: Mon, 11 Dec 2017 15:05:44 +0200 Subject: [PATCH 02/85] Fixed condition with usage isPost method - removed private function isPost - removed unused imports - added request method isPost - added interface that expands HTTP request class - fixed unit test in Contact module - fixed integration tests in Contact module --- .../Magento/Contact/Controller/Index/Post.php | 13 +---- .../Test/Unit/Controller/Index/PostTest.php | 6 ++- .../Magento/Contact/Controller/IndexTest.php | 4 ++ .../Framework/App/HttpRequestInterface.php | 52 +++++++++++++++++++ .../Magento/Framework/App/Request/Http.php | 3 +- 5 files changed, 64 insertions(+), 14 deletions(-) create mode 100644 lib/internal/Magento/Framework/App/HttpRequestInterface.php diff --git a/app/code/Magento/Contact/Controller/Index/Post.php b/app/code/Magento/Contact/Controller/Index/Post.php index ee2d23b74df24..4392cb2148a51 100644 --- a/app/code/Magento/Contact/Controller/Index/Post.php +++ b/app/code/Magento/Contact/Controller/Index/Post.php @@ -12,7 +12,6 @@ use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Framework\Controller\Result\Redirect; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\HTTP\PhpEnvironment\Request; use Psr\Log\LoggerInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject; @@ -67,7 +66,7 @@ public function __construct( */ public function execute() { - if (!$this->isPostRequest()) { + if (!$this->getRequest()->isPost()) { return $this->resultRedirectFactory->create()->setPath('*/*/'); } try { @@ -101,16 +100,6 @@ private function sendEmail($post) ); } - /** - * @return bool - */ - private function isPostRequest() - { - /** @var Request $request */ - $request = $this->getRequest(); - return !empty($request->getPostValue()); - } - /** * @return array * @throws \Exception diff --git a/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php b/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php index 0e1ddf21c2a08..0213173737b9b 100644 --- a/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php +++ b/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php @@ -78,7 +78,7 @@ protected function setUp() $this->createMock(\Magento\Framework\Message\ManagerInterface::class); $this->requestStub = $this->createPartialMock( \Magento\Framework\App\Request\Http::class, - ['getPostValue', 'getParams', 'getParam'] + ['getPostValue', 'getParams', 'getParam', 'isPost'] ); $this->redirectResultMock = $this->createMock(\Magento\Framework\Controller\Result\Redirect::class); $this->redirectResultMock->method('setPath')->willReturnSelf(); @@ -174,6 +174,10 @@ public function testExecuteValidPost() */ private function stubRequestPostData($post) { + $this->requestStub + ->expects($this->once()) + ->method('isPost') + ->willReturn(!empty($post)); $this->requestStub->method('getPostValue')->willReturn($post); $this->requestStub->method('getParams')->willReturn($post); $this->requestStub->method('getParam')->willReturnCallback( diff --git a/dev/tests/integration/testsuite/Magento/Contact/Controller/IndexTest.php b/dev/tests/integration/testsuite/Magento/Contact/Controller/IndexTest.php index 2d76632cae0b7..1275e62896469 100644 --- a/dev/tests/integration/testsuite/Magento/Contact/Controller/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Contact/Controller/IndexTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Contact\Controller; +use Zend\Http\Request; + /** * Contact index controller test */ @@ -19,6 +21,7 @@ public function testPostAction() 'hideit' => '', ]; $this->getRequest()->setPostValue($params); + $this->getRequest()->setMethod(Request::METHOD_POST); $this->dispatch('contact/index/post'); $this->assertRedirect($this->stringContains('contact/index')); @@ -38,6 +41,7 @@ public function testPostAction() public function testInvalidPostAction($params, $expectedMessage) { $this->getRequest()->setPostValue($params); + $this->getRequest()->setMethod(Request::METHOD_POST); $this->dispatch('contact/index/post'); $this->assertRedirect($this->stringContains('contact/index')); diff --git a/lib/internal/Magento/Framework/App/HttpRequestInterface.php b/lib/internal/Magento/Framework/App/HttpRequestInterface.php new file mode 100644 index 0000000000000..685db5f47ffc7 --- /dev/null +++ b/lib/internal/Magento/Framework/App/HttpRequestInterface.php @@ -0,0 +1,52 @@ + Date: Sat, 30 Dec 2017 22:39:26 -0500 Subject: [PATCH 03/85] Add wrapper function for newrelic_set_appname --- .../NewRelicReporting/Model/NewRelicWrapper.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php b/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php index 845ed0429d2c3..0d7bf630a5a84 100644 --- a/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php +++ b/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php @@ -41,6 +41,19 @@ public function reportError($exception) } } + /** + * Wrapper for 'newrelic_set_appname' + * + * @param string $appName + * @return void + */ + public function setAppName($appName) + { + if (extension_loaded('newrelic')) { + newrelic_set_appname($appName); + } + } + /** * Checks whether newrelic-php5 agent is installed * From 48066b29a13b3679922d5a3677c8e9c6dc5de20f Mon Sep 17 00:00:00 2001 From: Max Chadwick Date: Sat, 30 Dec 2017 22:40:22 -0500 Subject: [PATCH 04/85] Add setting --- app/code/Magento/NewRelicReporting/etc/adminhtml/system.xml | 5 +++++ app/code/Magento/NewRelicReporting/i18n/en_US.csv | 2 ++ 2 files changed, 7 insertions(+) diff --git a/app/code/Magento/NewRelicReporting/etc/adminhtml/system.xml b/app/code/Magento/NewRelicReporting/etc/adminhtml/system.xml index 582b7c752386a..98f9c55adbdf0 100644 --- a/app/code/Magento/NewRelicReporting/etc/adminhtml/system.xml +++ b/app/code/Magento/NewRelicReporting/etc/adminhtml/system.xml @@ -46,6 +46,11 @@ This is located by navigating to Settings from the New Relic APM website + + + Magento\Config\Model\Config\Source\Yesno + In addition to the main app (which includes all PHP execution), separate apps for adminhtml and frontend will be created. Requires New Relic Application Name to be set. + diff --git a/app/code/Magento/NewRelicReporting/i18n/en_US.csv b/app/code/Magento/NewRelicReporting/i18n/en_US.csv index 433b1b22fcddd..5ea64d3d43439 100644 --- a/app/code/Magento/NewRelicReporting/i18n/en_US.csv +++ b/app/code/Magento/NewRelicReporting/i18n/en_US.csv @@ -21,3 +21,5 @@ General,General "This is located by navigating to Settings from the New Relic APM website","This is located by navigating to Settings from the New Relic APM website" Cron,Cron "Enable Cron","Enable Cron" +"Send Adminhtml and Frontend as Separate Apps","Send Adminhtml and Frontend as Separate Apps" +"In addition to the main app (which includes all PHP execution), separate apps for adminhtml and frontend will be created. Requires New Relic Application Name to be set.","In addition to the main app (which includes all PHP execution), separate apps for adminhtml and frontend will be created. Requires New Relic Application Name to be set." From 7ceace8381df70632c4105bc8598bb1bee125afb Mon Sep 17 00:00:00 2001 From: Max Chadwick Date: Sat, 30 Dec 2017 22:40:44 -0500 Subject: [PATCH 05/85] Add method to consult setting --- app/code/Magento/NewRelicReporting/Model/Config.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/code/Magento/NewRelicReporting/Model/Config.php b/app/code/Magento/NewRelicReporting/Model/Config.php index 32e1078c01c9d..bcc87ec72d53f 100644 --- a/app/code/Magento/NewRelicReporting/Model/Config.php +++ b/app/code/Magento/NewRelicReporting/Model/Config.php @@ -161,6 +161,16 @@ public function getNewRelicAppName() return (string)$this->scopeConfig->getValue('newrelicreporting/general/app_name'); } + /** + * Returns configured separate apps value + * + * @return bool + */ + public function isSeparateApps() + { + return (bool)$this->scopeConfig->getValue('newrelicreporting/general/separate_apps'); + } + /** * Returns config setting for overall cron to be enabled * From 0b68496f17ca8488067c6630f1d8c362b3acc400 Mon Sep 17 00:00:00 2001 From: Max Chadwick Date: Sat, 30 Dec 2017 22:41:26 -0500 Subject: [PATCH 06/85] Add mechanics for separate appnames --- .../NewRelicReporting/Plugin/StatePlugin.php | 83 +++++++++++++++++++ app/code/Magento/NewRelicReporting/etc/di.xml | 3 + 2 files changed, 86 insertions(+) create mode 100644 app/code/Magento/NewRelicReporting/Plugin/StatePlugin.php diff --git a/app/code/Magento/NewRelicReporting/Plugin/StatePlugin.php b/app/code/Magento/NewRelicReporting/Plugin/StatePlugin.php new file mode 100644 index 0000000000000..149517bc7ce3f --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Plugin/StatePlugin.php @@ -0,0 +1,83 @@ +config = $config; + $this->newRelicWrapper = $newRelicWrapper; + } + + /** + * Set separate appname + * + * @param State $subject + * @param null $result + * @return void + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterSetAreaCode(State $state, $result) + { + if (!$this->shouldSetAppName()) { + return; + } + + try { + $this->newRelicWrapper->setAppName($this->appName($state)); + } catch (LocalizedException $e) { + return; + } + } + + private function appName(State $state) + { + $code = $state->getAreaCode(); + $current = $this->config->getNewRelicAppName(); + + return $current . ';' . $current . '_' . $code; + } + + private function shouldSetAppName() + { + if (!$this->config->isNewRelicEnabled()) { + return false; + } + + if (!$this->config->getNewRelicAppName()) { + return false; + } + + if (!$this->config->isSeparateApps()) { + return false; + } + + return true; + } +} diff --git a/app/code/Magento/NewRelicReporting/etc/di.xml b/app/code/Magento/NewRelicReporting/etc/di.xml index 2dccc45c1129b..bab7d6611f14b 100644 --- a/app/code/Magento/NewRelicReporting/etc/di.xml +++ b/app/code/Magento/NewRelicReporting/etc/di.xml @@ -30,6 +30,9 @@ + + + From e186221a9de870362bbdbbdf1b307675bad52e69 Mon Sep 17 00:00:00 2001 From: Max Chadwick Date: Mon, 26 Feb 2018 21:22:06 -0500 Subject: [PATCH 07/85] Add type hint --- app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php b/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php index 0d7bf630a5a84..ec21e06976b8b 100644 --- a/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php +++ b/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php @@ -47,7 +47,7 @@ public function reportError($exception) * @param string $appName * @return void */ - public function setAppName($appName) + public function setAppName(string $appName) { if (extension_loaded('newrelic')) { newrelic_set_appname($appName); From dc4830676f2c87be145050f2475edcc5b52472d7 Mon Sep 17 00:00:00 2001 From: Max Chadwick Date: Mon, 26 Feb 2018 21:38:05 -0500 Subject: [PATCH 08/85] Log exceptions This would happen if for some reason the area code wasn't set --- .../Magento/NewRelicReporting/Plugin/StatePlugin.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/NewRelicReporting/Plugin/StatePlugin.php b/app/code/Magento/NewRelicReporting/Plugin/StatePlugin.php index 149517bc7ce3f..b3f3237256be6 100644 --- a/app/code/Magento/NewRelicReporting/Plugin/StatePlugin.php +++ b/app/code/Magento/NewRelicReporting/Plugin/StatePlugin.php @@ -9,6 +9,7 @@ use Magento\Framework\Exception\LocalizedException; use Magento\NewRelicReporting\Model\Config; use Magento\NewRelicReporting\Model\NewRelicWrapper; +use Psr\Log\LoggerInterface; class StatePlugin { @@ -22,16 +23,23 @@ class StatePlugin */ private $newRelicWrapper; + /** + * @var LoggerInterface + */ + private $logger; + /** * @param Config $config * @param NewRelicWrapper $newRelicWrapper */ public function __construct( Config $config, - NewRelicWrapper $newRelicWrapper + NewRelicWrapper $newRelicWrapper, + LoggerInterface $logger ) { $this->config = $config; $this->newRelicWrapper = $newRelicWrapper; + $this->logger = $logger; } /** @@ -52,6 +60,7 @@ public function afterSetAreaCode(State $state, $result) try { $this->newRelicWrapper->setAppName($this->appName($state)); } catch (LocalizedException $e) { + $this->logger->critical($e); return; } } From bef36de64e6f0f0d1c0e55780809569500fec33f Mon Sep 17 00:00:00 2001 From: Max Chadwick Date: Mon, 26 Feb 2018 21:40:49 -0500 Subject: [PATCH 09/85] Update returns --- app/code/Magento/NewRelicReporting/Plugin/StatePlugin.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/NewRelicReporting/Plugin/StatePlugin.php b/app/code/Magento/NewRelicReporting/Plugin/StatePlugin.php index b3f3237256be6..0be7c72689e7f 100644 --- a/app/code/Magento/NewRelicReporting/Plugin/StatePlugin.php +++ b/app/code/Magento/NewRelicReporting/Plugin/StatePlugin.php @@ -54,14 +54,14 @@ public function __construct( public function afterSetAreaCode(State $state, $result) { if (!$this->shouldSetAppName()) { - return; + return $result; } try { $this->newRelicWrapper->setAppName($this->appName($state)); } catch (LocalizedException $e) { $this->logger->critical($e); - return; + return $result; } } From edd6062d2f40056d0a0a376fd62bbe8b67a06437 Mon Sep 17 00:00:00 2001 From: Andreas von Studnitz Date: Tue, 27 Feb 2018 12:04:35 +0100 Subject: [PATCH 10/85] Add a link to the success message when adding a product to the compare list --- .../Catalog/Controller/Product/Compare/Add.php | 9 ++++++++- app/code/Magento/Catalog/etc/frontend/di.xml | 12 ++++++++++++ .../messages/addCompareAddSuccessMessage.phtml | 14 ++++++++++++++ .../Catalog/Controller/Product/CompareTest.php | 2 +- 4 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Catalog/view/frontend/templates/messages/addCompareAddSuccessMessage.phtml diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php index 89eb6c9be929f..f7e163f53e6b2 100644 --- a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php +++ b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php @@ -36,7 +36,14 @@ public function execute() $productName = $this->_objectManager->get( \Magento\Framework\Escaper::class )->escapeHtml($product->getName()); - $this->messageManager->addSuccess(__('You added product %1 to the comparison list.', $productName)); + $this->messageManager->addComplexSuccessMessage( + 'addCompareAddSuccessMessage', + [ + 'product_name' => $productName, + 'compare_list_url' => $this->_url->getUrl('catalog/product_compare') + ] + ); + $this->_eventManager->dispatch('catalog_product_compare_add_product', ['product' => $product]); } diff --git a/app/code/Magento/Catalog/etc/frontend/di.xml b/app/code/Magento/Catalog/etc/frontend/di.xml index 63fc11c08d8bb..5cc198bcc2b13 100644 --- a/app/code/Magento/Catalog/etc/frontend/di.xml +++ b/app/code/Magento/Catalog/etc/frontend/di.xml @@ -79,4 +79,16 @@ recently_compared_product + + + + + \Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE + + Magento_Catalog::messages/addCompareAddSuccessMessage.phtml + + + + + diff --git a/app/code/Magento/Catalog/view/frontend/templates/messages/addCompareAddSuccessMessage.phtml b/app/code/Magento/Catalog/view/frontend/templates/messages/addCompareAddSuccessMessage.phtml new file mode 100644 index 0000000000000..5f44c42e17c57 --- /dev/null +++ b/app/code/Magento/Catalog/view/frontend/templates/messages/addCompareAddSuccessMessage.phtml @@ -0,0 +1,14 @@ + +escapeHtml(__( + 'You added product %1 to the comparison list.', + $block->getData('product_name'), + $block->getData('compare_list_url')), + ['a'] +); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php index a2a333df330fe..53df4c3b13e9e 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php @@ -47,7 +47,7 @@ public function testAddAction() ); $this->assertSessionMessages( - $this->equalTo(['You added product Simple Product 1 Name to the comparison list.']), + $this->equalTo(['You added product Simple Product 1 Name to the comparison list.']), MessageInterface::TYPE_SUCCESS ); From 6c17f9942701d6a7e380e9587301a90a96a00917 Mon Sep 17 00:00:00 2001 From: Andreas von Studnitz Date: Tue, 27 Feb 2018 13:20:03 +0100 Subject: [PATCH 11/85] Renaming to avoid duplication --- app/code/Magento/Catalog/Controller/Product/Compare/Add.php | 2 +- app/code/Magento/Catalog/etc/frontend/di.xml | 4 ++-- ...AddSuccessMessage.phtml => addCompareSuccessMessage.phtml} | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename app/code/Magento/Catalog/view/frontend/templates/messages/{addCompareAddSuccessMessage.phtml => addCompareSuccessMessage.phtml} (100%) diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php index f7e163f53e6b2..eb9cc83125541 100644 --- a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php +++ b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php @@ -37,7 +37,7 @@ public function execute() \Magento\Framework\Escaper::class )->escapeHtml($product->getName()); $this->messageManager->addComplexSuccessMessage( - 'addCompareAddSuccessMessage', + 'addCompareSuccessMessage', [ 'product_name' => $productName, 'compare_list_url' => $this->_url->getUrl('catalog/product_compare') diff --git a/app/code/Magento/Catalog/etc/frontend/di.xml b/app/code/Magento/Catalog/etc/frontend/di.xml index 5cc198bcc2b13..7ac3fa2c32102 100644 --- a/app/code/Magento/Catalog/etc/frontend/di.xml +++ b/app/code/Magento/Catalog/etc/frontend/di.xml @@ -82,10 +82,10 @@ - + \Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE - Magento_Catalog::messages/addCompareAddSuccessMessage.phtml + Magento_Catalog::messages/addCompareSuccessMessage.phtml diff --git a/app/code/Magento/Catalog/view/frontend/templates/messages/addCompareAddSuccessMessage.phtml b/app/code/Magento/Catalog/view/frontend/templates/messages/addCompareSuccessMessage.phtml similarity index 100% rename from app/code/Magento/Catalog/view/frontend/templates/messages/addCompareAddSuccessMessage.phtml rename to app/code/Magento/Catalog/view/frontend/templates/messages/addCompareSuccessMessage.phtml From 8141f17a6b28856f768d71a66091fc1f8857e642 Mon Sep 17 00:00:00 2001 From: Max Chadwick Date: Thu, 1 Mar 2018 22:39:38 -0500 Subject: [PATCH 12/85] Add a test --- .../Plugin/SeparateAppsTest.php | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/NewRelicReporting/Plugin/SeparateAppsTest.php diff --git a/dev/tests/integration/testsuite/Magento/NewRelicReporting/Plugin/SeparateAppsTest.php b/dev/tests/integration/testsuite/Magento/NewRelicReporting/Plugin/SeparateAppsTest.php new file mode 100644 index 0000000000000..3850a8cb3f9ae --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/NewRelicReporting/Plugin/SeparateAppsTest.php @@ -0,0 +1,47 @@ +objectManager = Bootstrap::getObjectManager(); + } + + /** + * @magentoConfigFixture default/newrelicreporting/general/enable 1 + * @magentoConfigFixture default/newrelicreporting/general/app_name beverly_hills + * @magentoConfigFixture default/newrelicreporting/general/separate_apps 1 + */ + public function testAppNameIsSetWhenConfiguredCorrectly() + { + $newRelicWrapper = $this->getMockBuilder(NewRelicWrapper::class) + ->setMethods(['setAppName']) + ->getMock(); + + $this->objectManager->configure([NewRelicWrapper::class => ['shared' => true]]); + $this->objectManager->addSharedInstance($newRelicWrapper, NewRelicWrapper::class); + + $newRelicWrapper->expects($this->once()) + ->method('setAppName') + ->with($this->equalTo('beverly_hills;beverly_hills_90210')); + + $state = $this->objectManager->get(State::class); + + $state->setAreaCode('90210'); + } +} From 0d350810fd112c53f802fb36d78cd034e404197f Mon Sep 17 00:00:00 2001 From: Lewis Voncken Date: Sat, 5 May 2018 23:17:36 +0200 Subject: [PATCH 13/85] [TASK] Solve issue #14966 - Disabling product does not remove it from the flat index --- .../Model/Indexer/Product/Flat/Action/Row.php | 58 +++++++++++++++---- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php index b5dbdb68606ff..e841e4a2651af 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php @@ -7,6 +7,8 @@ use Magento\Catalog\Model\Indexer\Product\Flat\FlatTableBuilder; use Magento\Catalog\Model\Indexer\Product\Flat\TableBuilder; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Catalog\Api\Data\ProductInterface; /** * Class Row reindex action @@ -22,6 +24,10 @@ class Row extends \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction * @var Eraser */ protected $flatItemEraser; + /** + * @var MetadataPool + */ + private $metadataPool; /** * @param \Magento\Framework\App\ResourceConnection $resource @@ -32,6 +38,7 @@ class Row extends \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction * @param FlatTableBuilder $flatTableBuilder * @param Indexer $flatItemWriter * @param Eraser $flatItemEraser + * @param MetadataPool $metadataPool */ public function __construct( \Magento\Framework\App\ResourceConnection $resource, @@ -41,7 +48,8 @@ public function __construct( TableBuilder $tableBuilder, FlatTableBuilder $flatTableBuilder, Indexer $flatItemWriter, - Eraser $flatItemEraser + Eraser $flatItemEraser, + MetadataPool $metadataPool ) { parent::__construct( $resource, @@ -53,6 +61,7 @@ public function __construct( ); $this->flatItemWriter = $flatItemWriter; $this->flatItemEraser = $flatItemEraser; + $this->metadataPool = $metadataPool; } /** @@ -75,18 +84,45 @@ public function execute($id = null) if ($tableExists) { $this->flatItemEraser->removeDeletedProducts($ids, $store->getId()); } - if (isset($ids[0])) { - if (!$tableExists) { - $this->_flatTableBuilder->build( - $store->getId(), - [$ids[0]], - $this->_valueFieldSuffix, - $this->_tableDropSuffix, - false - ); + + /* @var $status \Magento\Eav\Model\Entity\Attribute */ + $status = $this->_productIndexerHelper->getAttribute('status'); + $statusTable = $status->getBackendTable(); + $statusConditions = [ + 'store_id IN(0,' . (int)$store->getId() . ')', + 'attribute_id = ' . (int)$status->getId(), + 'entity_id = ' . (int)$id + ]; + $select = $this->_connection->select(); + $select->from( + $statusTable, + ['value'] + )->where( + implode(' AND ', $statusConditions) + )->order( + 'store_id DESC' + ); + $result = $this->_connection->query($select); + $status = $result->fetch(1); + + if ($status['value'] == \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) { + if (isset($ids[0])) { + if (!$tableExists) { + $this->_flatTableBuilder->build( + $store->getId(), + [$ids[0]], + $this->_valueFieldSuffix, + $this->_tableDropSuffix, + false + ); + } + $this->flatItemWriter->write($store->getId(), $ids[0], $this->_valueFieldSuffix); } - $this->flatItemWriter->write($store->getId(), $ids[0], $this->_valueFieldSuffix); + } else { + $this->flatItemEraser->deleteProductsFromStore($id, $store->getId()); } + + } return $this; } From c6196d7121569c1f82615ca6dcb9626d9285e972 Mon Sep 17 00:00:00 2001 From: Lewis Voncken Date: Sat, 5 May 2018 23:34:52 +0200 Subject: [PATCH 14/85] [TASK] Cleaned up incorrect dependency injection --- .../Catalog/Model/Indexer/Product/Flat/Action/Row.php | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php index e841e4a2651af..79769cef01132 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php @@ -7,8 +7,6 @@ use Magento\Catalog\Model\Indexer\Product\Flat\FlatTableBuilder; use Magento\Catalog\Model\Indexer\Product\Flat\TableBuilder; -use Magento\Framework\EntityManager\MetadataPool; -use Magento\Catalog\Api\Data\ProductInterface; /** * Class Row reindex action @@ -24,10 +22,6 @@ class Row extends \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction * @var Eraser */ protected $flatItemEraser; - /** - * @var MetadataPool - */ - private $metadataPool; /** * @param \Magento\Framework\App\ResourceConnection $resource @@ -38,7 +32,6 @@ class Row extends \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction * @param FlatTableBuilder $flatTableBuilder * @param Indexer $flatItemWriter * @param Eraser $flatItemEraser - * @param MetadataPool $metadataPool */ public function __construct( \Magento\Framework\App\ResourceConnection $resource, @@ -48,8 +41,7 @@ public function __construct( TableBuilder $tableBuilder, FlatTableBuilder $flatTableBuilder, Indexer $flatItemWriter, - Eraser $flatItemEraser, - MetadataPool $metadataPool + Eraser $flatItemEraser ) { parent::__construct( $resource, @@ -61,7 +53,6 @@ public function __construct( ); $this->flatItemWriter = $flatItemWriter; $this->flatItemEraser = $flatItemEraser; - $this->metadataPool = $metadataPool; } /** From 2d6fb6253dff51c91bd6107ddc4bb7e74bf7e618 Mon Sep 17 00:00:00 2001 From: Lewis Voncken Date: Sun, 6 May 2018 19:50:54 +0200 Subject: [PATCH 15/85] [TASK] Updated according to Codacy/PR Quality Review --- .../Catalog/Model/Indexer/Product/Flat/Action/Row.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php index 79769cef01132..d39995250e2d8 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php @@ -61,6 +61,7 @@ public function __construct( * @param int|null $id * @return \Magento\Catalog\Model\Indexer\Product\Flat\Action\Row * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Zend_Db_Statement_Exception */ public function execute($id = null) { @@ -109,11 +110,10 @@ public function execute($id = null) } $this->flatItemWriter->write($store->getId(), $ids[0], $this->_valueFieldSuffix); } - } else { + } + if ($status['value'] == \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED) { $this->flatItemEraser->deleteProductsFromStore($id, $store->getId()); } - - } return $this; } From 38bd9d381bc14ecc337a6707d96a767da7148f98 Mon Sep 17 00:00:00 2001 From: Lewis Voncken Date: Sun, 6 May 2018 20:49:02 +0200 Subject: [PATCH 16/85] [TASK] Updated the Unit Test according to issue-14966 --- .../Model/Indexer/Product/Flat/Action/Row.php | 2 +- .../Indexer/Product/Flat/Action/RowTest.php | 32 +++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php index d39995250e2d8..709f27d031ebe 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php @@ -79,7 +79,7 @@ public function execute($id = null) /* @var $status \Magento\Eav\Model\Entity\Attribute */ $status = $this->_productIndexerHelper->getAttribute('status'); - $statusTable = $status->getBackendTable(); + $statusTable = $status->getBackend()->getTable(); $statusConditions = [ 'store_id IN(0,' . (int)$store->getId() . ')', 'attribute_id = ' . (int)$status->getId(), diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php index 41b3d36227431..4bd931cd2d216 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php @@ -61,6 +61,8 @@ protected function setUp() { $objectManager = new ObjectManager($this); + $attributeTable = 'catalog_product_entity_int'; + $statusId = 22; $this->connection = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); $this->resource = $this->createMock(\Magento\Framework\App\ResourceConnection::class); $this->resource->expects($this->any())->method('getConnection') @@ -70,10 +72,36 @@ protected function setUp() $this->store = $this->createMock(\Magento\Store\Model\Store::class); $this->store->expects($this->any())->method('getId')->will($this->returnValue('store_id_1')); $this->storeManager->expects($this->any())->method('getStores')->will($this->returnValue([$this->store])); - $this->productIndexerHelper = $this->createMock(\Magento\Catalog\Helper\Product\Flat\Indexer::class); $this->flatItemEraser = $this->createMock(\Magento\Catalog\Model\Indexer\Product\Flat\Action\Eraser::class); $this->flatItemWriter = $this->createMock(\Magento\Catalog\Model\Indexer\Product\Flat\Action\Indexer::class); $this->flatTableBuilder = $this->createMock(\Magento\Catalog\Model\Indexer\Product\Flat\FlatTableBuilder::class); + $this->productIndexerHelper = $this->createMock(\Magento\Catalog\Helper\Product\Flat\Indexer::class); + $statusAttributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute::class) + ->disableOriginalConstructor() + ->getMock(); + $this->productIndexerHelper->expects($this->any())->method('getAttribute') + ->with('status') + ->willReturn($statusAttributeMock); + $backendMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend::class) + ->disableOriginalConstructor() + ->getMock(); + $backendMock->expects($this->any())->method('getTable')->willReturn($attributeTable); + $statusAttributeMock->expects($this->any())->method('getBackend')->willReturn( + $backendMock + ); + $statusAttributeMock->expects($this->any())->method('getId')->willReturn($statusId); + $selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + ->disableOriginalConstructor() + ->getMock(); + $this->connection->expects($this->any())->method('select')->willReturn($selectMock); + $selectMock->expects($this->any())->method('from')->with( + $attributeTable, + ['value'] + )->willReturnSelf(); + $selectMock->expects($this->any())->method('where')->willReturnSelf(); + $pdoMock = $this->createMock(\Zend_Db_Statement_Pdo::class); + $this->connection->expects($this->any())->method('query')->with($selectMock)->will($this->returnValue($pdoMock)); + $pdoMock->expects($this->any())->method('fetch')->will($this->returnValue(['value' => 1])); $this->model = $objectManager->getObject( \Magento\Catalog\Model\Indexer\Product\Flat\Action\Row::class, [ @@ -82,7 +110,7 @@ protected function setUp() 'productHelper' => $this->productIndexerHelper, 'flatItemEraser' => $this->flatItemEraser, 'flatItemWriter' => $this->flatItemWriter, - 'flatTableBuilder' => $this->flatTableBuilder + 'flatTableBuilder' => $this->flatTableBuilder, ]); } From 219024c2e3d9b2355fdfb4a8c811cfd172cebba5 Mon Sep 17 00:00:00 2001 From: Eugene Shakhsuvarov Date: Mon, 7 May 2018 17:31:32 +0300 Subject: [PATCH 17/85] Disabling product doesn't remove from flat table - Suppresses coupling warning in unit test --- .../Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php index 4bd931cd2d216..2572356cf855d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php @@ -10,6 +10,9 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class RowTest extends \PHPUnit\Framework\TestCase { /** From 8c9f80bc6d2a2c83719b3eedbd519cbe73fcf168 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Tue, 15 May 2018 19:33:12 +0300 Subject: [PATCH 18/85] MAGETWO-86125: Sorting on price of configurable products in catalog not working properly --- .../Product/Indexer/Price/DefaultPrice.php | 6 +- .../Model/Indexer/ProductPriceIndexFilter.php | 81 +++++++++++++++++++ ...datePriceIndexUponConfigChangeObserver.php | 44 ++++++++++ app/code/Magento/CatalogInventory/etc/di.xml | 7 ++ .../Magento/CatalogInventory/etc/events.xml | 1 + .../Magento/CatalogInventory/etc/indexer.xml | 5 ++ .../Catalog/Model/ProductPriceTest.php | 34 +++++++- .../Framework/Indexer/Config/Reader.php | 1 + 8 files changed, 174 insertions(+), 5 deletions(-) create mode 100644 app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php create mode 100644 app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php index 1de0a480c9fbc..edf2a3a1c05bd 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php @@ -307,7 +307,7 @@ protected function prepareFinalPriceDataForType($entityIds, $type) $query = $select->insertFromSelect($finalPriceTable->getTableName(), [], false); $this->getConnection()->query($query); - $this->applyDiscountPrices($finalPriceTable); + $this->modifyPriceIndex($finalPriceTable); return $this; } @@ -512,12 +512,12 @@ protected function _prepareCustomOptionPriceTable() } /** - * Apply discount prices to final price index table. + * Modify data in price index table. * * @param IndexTableStructure $finalPriceTable * @return void */ - private function applyDiscountPrices(IndexTableStructure $finalPriceTable) + private function modifyPriceIndex(IndexTableStructure $finalPriceTable) { foreach ($this->priceModifiers as $priceModifier) { $priceModifier->modifyPrice($finalPriceTable); diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php new file mode 100644 index 0000000000000..51a4fc30ac4ba --- /dev/null +++ b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php @@ -0,0 +1,81 @@ +stockConfiguration = $stockConfiguration; + $this->stockStatus = $stockStatus; + } + + /** + * Remove out of stock products data from price index. + * + * @param IndexTableStructure $priceTable + * @param array $entityIds + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []) + { + if ($this->stockConfiguration->isShowOutOfStock()) { + return; + } + + $connection = $this->stockStatus->getConnection(); + $select = $connection->select(); + $select->from( + ['price_index' => $priceTable->getTableName()], + '' + ); + $select->joinLeft( + ['website_stock' => $this->stockStatus->getMainTable()], + 'website_stock.product_id = price_index.' . $priceTable->getEntityField() + . ' AND website_stock.website_id = price_index.' . $priceTable->getWebsiteField() + . ' AND website_stock.stock_id = ' . Stock::DEFAULT_STOCK_ID, + '' + ); + $select->joinLeft( + ['default_stock' => $this->stockStatus->getMainTable()], + 'default_stock.product_id = price_index.' . $priceTable->getEntityField() + . ' AND default_stock.website_id = 0' + . ' AND default_stock.stock_id = ' . Stock::DEFAULT_STOCK_ID, + '' + ); + $stockStatus = $connection->getIfNullSql('website_stock.stock_status', 'default_stock.stock_status'); + $select->where($stockStatus . ' = ?', 0); + + $query = $select->deleteFromSelect('price_index'); + $connection->query($query); + } +} diff --git a/app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php b/app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php new file mode 100644 index 0000000000000..78b4cdfe26988 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php @@ -0,0 +1,44 @@ +priceIndexProcessor = $priceIndexProcessor; + } + + /** + * Invalidate product price index on catalog inventory config changes. + * + * @param Observer $observer + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function execute(Observer $observer) + { + $priceIndexer = $this->priceIndexProcessor->getIndexer(); + $priceIndexer->invalidate(); + } +} diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml index 65bc277121429..0cdf25e90ce63 100644 --- a/app/code/Magento/CatalogInventory/etc/di.xml +++ b/app/code/Magento/CatalogInventory/etc/di.xml @@ -118,4 +118,11 @@ + + + + Magento\CatalogInventory\Model\Indexer\ProductPriceIndexFilter + + + diff --git a/app/code/Magento/CatalogInventory/etc/events.xml b/app/code/Magento/CatalogInventory/etc/events.xml index 3197501e9b70b..328edbade6068 100644 --- a/app/code/Magento/CatalogInventory/etc/events.xml +++ b/app/code/Magento/CatalogInventory/etc/events.xml @@ -38,6 +38,7 @@ + diff --git a/app/code/Magento/CatalogInventory/etc/indexer.xml b/app/code/Magento/CatalogInventory/etc/indexer.xml index 725477f64908f..af23d79bc2925 100644 --- a/app/code/Magento/CatalogInventory/etc/indexer.xml +++ b/app/code/Magento/CatalogInventory/etc/indexer.xml @@ -10,4 +10,9 @@ Stock Index stock + + + + + diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php index a33b70bbd0160..64719a8b289df 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php @@ -8,6 +8,7 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\CatalogInventory\Api\StockRegistryInterface; /** * Tests product model: @@ -23,9 +24,15 @@ class ProductPriceTest extends \PHPUnit\Framework\TestCase */ protected $_model; + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + protected function setUp() { $this->_model = Bootstrap::getObjectManager()->create(Product::class); + $this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); } public function testGetPrice() @@ -80,8 +87,7 @@ public function testSetGetFinalPrice() */ public function testGetMinPrice() { - $productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); - $product = $productRepository->get('simple'); + $product = $this->productRepository->get('simple'); $collection = Bootstrap::getObjectManager()->create(Collection::class); $collection->addIdFilter($product->getId()); $collection->addPriceData(); @@ -90,4 +96,28 @@ public function testGetMinPrice() $product = $collection->getFirstItem(); $this->assertEquals(333, $product->getData('min_price')); } + + /** + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable_sku.php + */ + public function testGetMinPriceForComposite() + { + $confProduct = $this->productRepository->get('configurable'); + $collection = Bootstrap::getObjectManager()->create(Collection::class); + $collection->addIdFilter($confProduct->getId()); + $collection->addPriceData(); + $collection->load(); + $product = $collection->getFirstItem(); + $this->assertEquals(10, $product->getData('min_price')); + + $childProduct = $this->productRepository->get('simple_10'); + $stockRegistry = Bootstrap::getObjectManager()->get(StockRegistryInterface::class); + $stockItem = $stockRegistry->getStockItem($childProduct->getId()); + $stockItem->setIsInStock(false); + $stockRegistry->updateStockItemBySku($childProduct->getSku(), $stockItem); + $collection->clear()->load(); + $product = $collection->getFirstItem(); + $this->assertEquals(20, $product->getData('min_price')); + } } diff --git a/lib/internal/Magento/Framework/Indexer/Config/Reader.php b/lib/internal/Magento/Framework/Indexer/Config/Reader.php index 6ef22b3b7f796..9ed35ef0e9af5 100644 --- a/lib/internal/Magento/Framework/Indexer/Config/Reader.php +++ b/lib/internal/Magento/Framework/Indexer/Config/Reader.php @@ -18,6 +18,7 @@ class Reader extends \Magento\Framework\Config\Reader\Filesystem '/config/indexer/source' => 'name', '/config/indexer/fieldset' => 'name', '/config/indexer/fieldset/field' => 'name', + '/config/indexer/dependencies/indexer' => 'id', ]; /** From 01414fcdfe8722ac08f22c2547092dc3d7b7b815 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Wed, 16 May 2018 12:22:47 +0300 Subject: [PATCH 19/85] MAGETWO-86125: Sorting on price of configurable products in catalog not working properly --- app/code/Magento/CatalogInventory/Model/StockRegistry.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/code/Magento/CatalogInventory/Model/StockRegistry.php b/app/code/Magento/CatalogInventory/Model/StockRegistry.php index d688132fdb916..0fb09d27baa79 100644 --- a/app/code/Magento/CatalogInventory/Model/StockRegistry.php +++ b/app/code/Magento/CatalogInventory/Model/StockRegistry.php @@ -171,7 +171,13 @@ public function updateStockItemBySku($productSku, \Magento\CatalogInventory\Api\ $productId = $this->resolveProductId($productSku); $websiteId = $stockItem->getWebsiteId() ?: null; $origStockItem = $this->getStockItem($productId, $websiteId); + $data = $stockItem->getData(); + $origData = $origStockItem->getData(); + if ($data === $origData) { + return $stockItem->getItemId(); + } + if ($origStockItem->getItemId()) { unset($data['item_id']); } From d43ef9c4d7760181889f45d2f5eb0ff12244f33a Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Wed, 16 May 2018 13:03:57 +0300 Subject: [PATCH 20/85] MAGETWO-86125: Sorting on price of configurable products in catalog not working properly --- .../CatalogInventory/Model/StockRegistry.php | 6 ---- .../Observer/SaveInventoryDataObserver.php | 30 +++++++++++++++---- .../_files/product_with_attribute.php | 4 ++- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/StockRegistry.php b/app/code/Magento/CatalogInventory/Model/StockRegistry.php index 0fb09d27baa79..d688132fdb916 100644 --- a/app/code/Magento/CatalogInventory/Model/StockRegistry.php +++ b/app/code/Magento/CatalogInventory/Model/StockRegistry.php @@ -171,13 +171,7 @@ public function updateStockItemBySku($productSku, \Magento\CatalogInventory\Api\ $productId = $this->resolveProductId($productSku); $websiteId = $stockItem->getWebsiteId() ?: null; $origStockItem = $this->getStockItem($productId, $websiteId); - $data = $stockItem->getData(); - $origData = $origStockItem->getData(); - if ($data === $origData) { - return $stockItem->getItemId(); - } - if ($origStockItem->getItemId()) { unset($data['item_id']); } diff --git a/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php b/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php index 03ba58d3f4987..d35573174ae9b 100644 --- a/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php +++ b/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php @@ -100,14 +100,34 @@ public function __construct( public function execute(EventObserver $observer) { $product = $observer->getEvent()->getProduct(); - $stockItem = $this->getStockItemToBeUpdated($product); - if ($product->getStockData() !== null) { + $stockItem = $this->getStockItemToBeUpdated($product); $stockData = $this->getStockData($product); - $stockItem->addData($stockData); + + if ($this->isStockDataChanged($stockItem, $stockData)) { + $stockItem->addData($stockData); + $this->stockItemValidator->validate($product, $stockItem); + $this->stockRegistry->updateStockItemBySku($product->getSku(), $stockItem); + } + } + } + + /** + * Check is stock item data changed + * + * @param Item $stockItem + * @param array $stockData + * @return bool + */ + private function isStockDataChanged(Item $stockItem, array $stockData) + { + foreach ($stockData as $field => $value) { + if ($stockItem->getData($field) !== $value) { + return true; + } } - $this->stockItemValidator->validate($product, $stockItem); - $this->stockRegistry->updateStockItemBySku($product->getSku(), $stockItem); + + return false; } /** diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php index 917d29b1ae5f2..c15561a93d776 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php @@ -87,7 +87,9 @@ ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setStockData([ 'use_config_manage_stock' => 1, - 'is_in_stock' => 0, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, ]); $configurableAttributesData = [ [ From 72ce41bbc14d90a96c5c0edfb54ab7a5ebc9dfd3 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Wed, 16 May 2018 17:50:59 +0300 Subject: [PATCH 21/85] MAGETWO-86125: Sorting on price of configurable products in catalog not working properly --- .../Observer/SaveInventoryDataObserver.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php b/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php index d35573174ae9b..be66bc941c8ea 100644 --- a/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php +++ b/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php @@ -100,16 +100,14 @@ public function __construct( public function execute(EventObserver $observer) { $product = $observer->getEvent()->getProduct(); + $stockItem = $this->getStockItemToBeUpdated($product); + if ($product->getStockData() !== null) { - $stockItem = $this->getStockItemToBeUpdated($product); $stockData = $this->getStockData($product); - - if ($this->isStockDataChanged($stockItem, $stockData)) { - $stockItem->addData($stockData); - $this->stockItemValidator->validate($product, $stockItem); - $this->stockRegistry->updateStockItemBySku($product->getSku(), $stockItem); - } + $stockItem->addData($stockData); } + $this->stockItemValidator->validate($product, $stockItem); + $this->stockRegistry->updateStockItemBySku($product->getSku(), $stockItem); } /** From db0610ab222c0225acaa3703eff6ebd15f46fabe Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Thu, 17 May 2018 11:51:16 +0300 Subject: [PATCH 22/85] MAGETWO-86125: Sorting on price of configurable products in catalog not working properly --- app/code/Magento/CatalogInventory/etc/mview.xml | 5 +++++ .../Magento/Indexer/Model/Indexer/DependencyDecorator.php | 5 ++++- .../Catalog/Model/ResourceModel/Product/CollectionTest.php | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogInventory/etc/mview.xml b/app/code/Magento/CatalogInventory/etc/mview.xml index c3d73ff43e8eb..03128d3b8d21b 100644 --- a/app/code/Magento/CatalogInventory/etc/mview.xml +++ b/app/code/Magento/CatalogInventory/etc/mview.xml @@ -11,4 +11,9 @@ + + +
+ + diff --git a/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php b/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php index d22795f127138..d0a2732bfaf1d 100644 --- a/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php +++ b/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php @@ -256,7 +256,10 @@ public function reindexRow($id) $this->indexer->reindexRow($id); $dependentIndexerIds = $this->dependencyInfoProvider->getIndexerIdsToRunAfter($this->indexer->getId()); foreach ($dependentIndexerIds as $indexerId) { - $this->indexerRegistry->get($indexerId)->reindexRow($id); + $dependentIndexer = $this->indexerRegistry->get($indexerId); + if (!$dependentIndexer->isScheduled()) { + $dependentIndexer->reindexRow($id); + } } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php index 7393c6ebc163f..a5e55e181cbaf 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php @@ -50,6 +50,7 @@ public function testAddPriceDataOnSchedule() { $this->processor->getIndexer()->setScheduled(true); $this->assertTrue($this->processor->getIndexer()->isScheduled()); + $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ @@ -81,7 +82,6 @@ public function testAddPriceDataOnSchedule() $product = reset($items); $this->assertCount(2, $items); $this->assertEquals(15, $product->getPrice()); - $this->processor->getIndexer()->reindexList([1]); $this->processor->getIndexer()->setScheduled(false); } From 9eb81c71a1e1baced47a7e33de8704dd39f64ace Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Thu, 17 May 2018 12:00:02 +0300 Subject: [PATCH 23/85] MAGETWO-86125: Sorting on price of configurable products in catalog not working properly --- .../Model/Indexer/ProductPriceIndexFilter.php | 2 +- .../Observer/SaveInventoryDataObserver.php | 18 ------------------ .../Magento/CatalogInventory/etc/mview.xml | 2 +- 3 files changed, 2 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php index 51a4fc30ac4ba..a76e98f9b1710 100644 --- a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php +++ b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php @@ -73,7 +73,7 @@ public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = '' ); $stockStatus = $connection->getIfNullSql('website_stock.stock_status', 'default_stock.stock_status'); - $select->where($stockStatus . ' = ?', 0); + $select->where($stockStatus . ' = ?', Stock::STOCK_OUT_OF_STOCK); $query = $select->deleteFromSelect('price_index'); $connection->query($query); diff --git a/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php b/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php index be66bc941c8ea..03ba58d3f4987 100644 --- a/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php +++ b/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php @@ -110,24 +110,6 @@ public function execute(EventObserver $observer) $this->stockRegistry->updateStockItemBySku($product->getSku(), $stockItem); } - /** - * Check is stock item data changed - * - * @param Item $stockItem - * @param array $stockData - * @return bool - */ - private function isStockDataChanged(Item $stockItem, array $stockData) - { - foreach ($stockData as $field => $value) { - if ($stockItem->getData($field) !== $value) { - return true; - } - } - - return false; - } - /** * Return the stock item that needs to be updated * diff --git a/app/code/Magento/CatalogInventory/etc/mview.xml b/app/code/Magento/CatalogInventory/etc/mview.xml index 03128d3b8d21b..72dda16e8b5bb 100644 --- a/app/code/Magento/CatalogInventory/etc/mview.xml +++ b/app/code/Magento/CatalogInventory/etc/mview.xml @@ -11,7 +11,7 @@
- +
From ab15b4bba3e96e2d88da3a16ed7b35dbb0607a6e Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Thu, 17 May 2018 12:56:41 +0300 Subject: [PATCH 24/85] MAGETWO-86125: Sorting on price of configurable products in catalog not working properly --- .../CatalogRule/Model/Indexer/Product/PriceTest.php | 5 +++++ .../Magento/CatalogRule/_files/product_with_attribute.php | 4 +--- .../_files/product_downloadable_with_files.php | 7 ++++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php index d28e765921d6e..22eebecac29c3 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php @@ -9,6 +9,7 @@ use Magento\CatalogRule\Model\ResourceModel\Rule; use Magento\Catalog\Model\ResourceModel\Product\Collection; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\CatalogInventory\Api\StockRegistryInterface; use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Api\SortOrder; @@ -49,6 +50,10 @@ public function testPriceApplying() $this->assertEquals($rulePrice, $simpleProduct->getFinalPrice()); $confProductId = 666; + $stockRegistry = Bootstrap::getObjectManager()->get(StockRegistryInterface::class); + $stockItem = $stockRegistry->getStockItem($confProductId); + $stockItem->setIsInStock(true); + $stockRegistry->updateStockItemBySku('configurable', $stockItem); $collection = Bootstrap::getObjectManager()->create(Collection::class); $collection->addIdFilter($confProductId); $collection->addPriceData($customerGroupId, $websiteId); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php index c15561a93d776..917d29b1ae5f2 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php @@ -87,9 +87,7 @@ ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setStockData([ 'use_config_manage_stock' => 1, - 'qty' => 100, - 'is_qty_decimal' => 0, - 'is_in_stock' => 1, + 'is_in_stock' => 0, ]); $configurableAttributesData = [ [ diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php index cf0da5599914f..c6fdcb607e2a8 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php @@ -53,7 +53,12 @@ \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH )->setStatus( \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED -); +)->setStockData([ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, +]); $extension = $product->getExtensionAttributes(); $links = []; From d6ad92ba37961f87af83b8c7a72451e67e07c303 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Thu, 17 May 2018 16:18:10 +0300 Subject: [PATCH 25/85] MAGETWO-86125: Sorting on price of configurable products in catalog not working properly --- .../Model/Indexer/Product/PriceTest.php | 19 +++++++------------ .../_files/product_with_attribute.php | 4 +++- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php index 22eebecac29c3..06053d13e071f 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php @@ -9,7 +9,6 @@ use Magento\CatalogRule\Model\ResourceModel\Rule; use Magento\Catalog\Model\ResourceModel\Product\Collection; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\CatalogInventory\Api\StockRegistryInterface; use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Api\SortOrder; @@ -50,10 +49,6 @@ public function testPriceApplying() $this->assertEquals($rulePrice, $simpleProduct->getFinalPrice()); $confProductId = 666; - $stockRegistry = Bootstrap::getObjectManager()->get(StockRegistryInterface::class); - $stockItem = $stockRegistry->getStockItem($confProductId); - $stockItem->setIsInStock(true); - $stockRegistry->updateStockItemBySku('configurable', $stockItem); $collection = Bootstrap::getObjectManager()->create(Collection::class); $collection->addIdFilter($confProductId); $collection->addPriceData($customerGroupId, $websiteId); @@ -75,13 +70,13 @@ public function testSortByPrice() $searchCriteria->setSortOrders([$sortOrder]); $productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); $searchResults = $productRepository->getList($searchCriteria); - $products = $searchResults->getItems(); + /** @var \Magento\Catalog\Model\Product[] $products */ + $products = array_values($searchResults->getItems()); + $this->assertTrue($products[0]->getMinimalPrice() <= $products[1]->getMinimalPrice()); + $this->assertTrue($products[1]->getMinimalPrice() <= $products[2]->getMinimalPrice()); - /** @var \Magento\Catalog\Model\Product $product1 */ - $product1 = array_values($products)[0]; - $product1->setPriceCalculation(false); - $this->assertEquals('simple1', $product1->getSku()); - $rulePrice = $this->resourceRule->getRulePrice(new \DateTime(), 1, 1, $product1->getId()); - $this->assertEquals($rulePrice, $product1->getFinalPrice()); + $products[0]->setPriceCalculation(false); + $rulePrice = $this->resourceRule->getRulePrice(new \DateTime(), 1, 1, $products[0]->getId()); + $this->assertEquals($rulePrice, $products[0]->getFinalPrice()); } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php index 917d29b1ae5f2..c15561a93d776 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php @@ -87,7 +87,9 @@ ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setStockData([ 'use_config_manage_stock' => 1, - 'is_in_stock' => 0, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, ]); $configurableAttributesData = [ [ From eb5cd29e7385b47709bd09afd3a6f47fe90fd108 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Thu, 17 May 2018 16:33:00 +0300 Subject: [PATCH 26/85] MAGETWO-86125: Sorting on price of configurable products in catalog not working properly --- app/code/Magento/CatalogRule/etc/mview.xml | 5 ++ .../Model/Indexer/Product/PriceTest.php | 31 ++++---- ...attribute.php => configurable_product.php} | 71 ++++--------------- .../_files/configurable_product_rollback.php | 31 ++++++++ .../CatalogRule/_files/simple_products.php | 54 ++++++++++++++ ...lback.php => simple_products_rollback.php} | 4 +- 6 files changed, 123 insertions(+), 73 deletions(-) rename dev/tests/integration/testsuite/Magento/CatalogRule/_files/{product_with_attribute.php => configurable_product.php} (51%) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php rename dev/tests/integration/testsuite/Magento/CatalogRule/_files/{product_with_attribute_rollback.php => simple_products_rollback.php} (87%) diff --git a/app/code/Magento/CatalogRule/etc/mview.xml b/app/code/Magento/CatalogRule/etc/mview.xml index 4b1166941bdc8..35efe33461afc 100644 --- a/app/code/Magento/CatalogRule/etc/mview.xml +++ b/app/code/Magento/CatalogRule/etc/mview.xml @@ -24,4 +24,9 @@
+ + +
+ + diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php index 06053d13e071f..495d19a2745e5 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php @@ -12,13 +12,6 @@ use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Api\SortOrder; -/** - * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/attribute.php - * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/product_with_attribute.php - * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/rule_by_attribute.php - * @magentoDbIsolation enabled - * @magentoAppIsolation enabled - */ class PriceTest extends \PHPUnit\Framework\TestCase { /** @@ -31,6 +24,12 @@ protected function setUp() $this->resourceRule = Bootstrap::getObjectManager()->get(Rule::class); } + /** + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/configurable_product.php + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/rule_by_attribute.php + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ public function testPriceApplying() { $customerGroupId = 1; @@ -44,7 +43,6 @@ public function testPriceApplying() /** @var \Magento\Catalog\Model\Product $simpleProduct */ $simpleProduct = $collection->getFirstItem(); $simpleProduct->setPriceCalculation(false); - $rulePrice = $this->resourceRule->getRulePrice(new \DateTime(), $websiteId, $customerGroupId, $simpleProductId); $this->assertEquals($rulePrice, $simpleProduct->getFinalPrice()); @@ -55,11 +53,14 @@ public function testPriceApplying() $collection->load(); /** @var \Magento\Catalog\Model\Product $confProduct */ $confProduct = $collection->getFirstItem(); - - $this->assertEquals($simpleProduct->getMinimalPrice(), $confProduct->getMinimalPrice()); + $this->assertEquals($simpleProduct->getFinalPrice(), $confProduct->getMinimalPrice()); } /** + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/simple_products.php + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/rule_by_attribute.php + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled * @magentoAppArea frontend */ public function testSortByPrice() @@ -72,11 +73,11 @@ public function testSortByPrice() $searchResults = $productRepository->getList($searchCriteria); /** @var \Magento\Catalog\Model\Product[] $products */ $products = array_values($searchResults->getItems()); - $this->assertTrue($products[0]->getMinimalPrice() <= $products[1]->getMinimalPrice()); - $this->assertTrue($products[1]->getMinimalPrice() <= $products[2]->getMinimalPrice()); - $products[0]->setPriceCalculation(false); - $rulePrice = $this->resourceRule->getRulePrice(new \DateTime(), 1, 1, $products[0]->getId()); - $this->assertEquals($rulePrice, $products[0]->getFinalPrice()); + $product1 = $products[0]; + $product1->setPriceCalculation(false); + $this->assertEquals('simple1', $product1->getSku()); + $rulePrice = $this->resourceRule->getRulePrice(new \DateTime(), 1, 1, $product1->getId()); + $this->assertEquals($rulePrice, $product1->getFinalPrice()); } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product.php similarity index 51% rename from dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php rename to dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product.php index c15561a93d776..971f9f82dc1ec 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product.php @@ -5,12 +5,12 @@ */ require __DIR__ . '/../../ConfigurableProduct/_files/configurable_attribute.php'; +require __DIR__ . '/simple_products.php'; $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $storeManager = $objectManager->get(\Magento\Store\Model\StoreManager::class); $store = $storeManager->getStore('default'); - $productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); $installer = $objectManager->get(\Magento\Catalog\Setup\CategorySetup::class); @@ -18,63 +18,22 @@ $attributeValues = []; $associatedProductIds = []; -/** @var Magento\Eav\Model\Entity\Attribute\Option[] $options */ +$attributeRepository = $objectManager->get(\Magento\Eav\Api\AttributeRepositoryInterface::class); +$attribute = $attributeRepository->get('catalog_product', 'test_configurable'); $options = $attribute->getOptions(); array_shift($options); //remove the first option which is empty - -$product = $objectManager->create(\Magento\Catalog\Model\Product::class) - ->setTypeId('simple') - ->setId(1) - ->setAttributeSetId($attributeSetId) - ->setWebsiteIds([1]) - ->setName('Simple Product 1') - ->setSku('simple1') - ->setPrice(10) - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setStockData([ - 'use_config_manage_stock' => 1, - 'qty' => 100, - 'is_qty_decimal' => 0, - 'is_in_stock' => 1, - ]); -$option = array_shift($options); -$product->setTestConfigurable($option->getValue()); -$productRepository->save($product); -$attributeValues[] = [ - 'label' => 'test', - 'attribute_id' => $attribute->getId(), - 'value_index' => $option->getValue(), -]; -$associatedProductIds[] = $product->getId(); -$productAction = $objectManager->get(\Magento\Catalog\Model\Product\Action::class); -$productAction->updateAttributes([$product->getId()], ['test_attribute' => 'test_attribute_value'], $store->getId()); - -$product = $objectManager->create(\Magento\Catalog\Model\Product::class) - ->setTypeId('simple') - ->setId(2) - ->setAttributeSetId($attributeSetId) - ->setWebsiteIds([1]) - ->setName('Simple Product 2') - ->setSku('simple2') - ->setPrice(9.9) - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setStockData([ - 'use_config_manage_stock' => 1, - 'qty' => 100, - 'is_qty_decimal' => 0, - 'is_in_stock' => 1, - ]); -$option = array_shift($options); -$product->setTestConfigurable($option->getValue()); -$productRepository->save($product); -$attributeValues[] = [ - 'label' => 'test', - 'attribute_id' => $attribute->getId(), - 'value_index' => $option->getValue(), -]; -$associatedProductIds[] = $product->getId(); +foreach (['simple1', 'simple2'] as $sku) { + $option = array_shift($options); + $product = $productRepository->get($sku); + $product->setTestConfigurable($option->getValue()); + $productRepository->save($product); + $attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; + $associatedProductIds[] = $product->getId(); +} $product = $objectManager->create(\Magento\Catalog\Model\Product::class) ->setTypeId('configurable') diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php new file mode 100644 index 0000000000000..d4311d523d3c7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php @@ -0,0 +1,31 @@ +getInstance()->reinitialize(); + +/** @var $objectManager \Magento\TestFramework\ObjectManager */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = $objectManager->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +try { + $product = $productRepository->get('configurable', false, null, true); + $productRepository->delete($product); +} catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + //Nothing to delete +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/simple_products_rollback.php'; +require __DIR__ . '/../../ConfigurableProduct/_files/configurable_attribute_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php new file mode 100644 index 0000000000000..2855ebb4c92b3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php @@ -0,0 +1,54 @@ +get(\Magento\Store\Model\StoreManager::class); +$store = $storeManager->getStore('default'); +$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +$installer = $objectManager->get(\Magento\Catalog\Setup\CategorySetup::class); +$attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default'); + +$product = $objectManager->create(\Magento\Catalog\Model\Product::class) + ->setTypeId('simple') + ->setId(1) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Simple Product 1') + ->setSku('simple1') + ->setPrice(10) + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData([ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ]); +$productRepository->save($product); +$productAction = $objectManager->get(\Magento\Catalog\Model\Product\Action::class); +$productAction->updateAttributes([$product->getId()], ['test_attribute' => 'test_attribute_value'], $store->getId()); + +$product = $objectManager->create(\Magento\Catalog\Model\Product::class) + ->setTypeId('simple') + ->setId(2) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Simple Product 2') + ->setSku('simple2') + ->setPrice(9.9) + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData([ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ]); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php similarity index 87% rename from dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute_rollback.php rename to dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php index 71b164ce6d67e..a46d1db297e2d 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute_rollback.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php @@ -17,7 +17,7 @@ /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ $productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); -foreach (['simple1', 'simple2', 'configurable'] as $sku) { +foreach (['simple1', 'simple2'] as $sku) { try { $product = $productRepository->get($sku, false, null, true); $productRepository->delete($product); @@ -29,4 +29,4 @@ $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); -require __DIR__ . '/../../ConfigurableProduct/_files/configurable_attribute_rollback.php'; +require __DIR__ . '/attribute_rollback.php'; From becbf6aa5a2fd7937caab55a84f463841eee0d59 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun Date: Fri, 18 May 2018 08:25:42 +0300 Subject: [PATCH 27/85] Fix typo in test method's name and test result --- .../Magento/Checkout/Test/Block/Onepage/Shipping/Method.php | 2 +- .../Test/Constraint/AssertCityBasedShippingRateChanged.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/Method.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/Method.php index 3a66629116de9..f5024d3eb7b26 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/Method.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/Method.php @@ -91,7 +91,7 @@ public function selectShippingMethod(array $method) * @param array $method * @return bool */ - public function isShippingMethodAvaiable(array $method) + public function isShippingMethodAvailable(array $method) { $this->waitForShippingRates(); $selector = sprintf($this->shippingMethod, $method['shipping_method'], $method['shipping_service']); diff --git a/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertCityBasedShippingRateChanged.php b/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertCityBasedShippingRateChanged.php index a449f79ef68f3..ab6233f1a440a 100644 --- a/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertCityBasedShippingRateChanged.php +++ b/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertCityBasedShippingRateChanged.php @@ -30,10 +30,10 @@ public function processAssert(CheckoutOnepage $checkoutOnepage, $shippingMethod, 'Shipping rate has not been changed.' ); } - $shippingAvaialability = $isShippingAvailable ? 'avaiable' : 'unavailable'; + $shippingAvaialability = $isShippingAvailable ? 'available' : 'unavailable'; \PHPUnit_Framework_Assert::assertEquals( $isShippingAvailable, - $checkoutOnepage->getShippingMethodBlock()->isShippingMethodAvaiable($shippingMethod), + $checkoutOnepage->getShippingMethodBlock()->isShippingMethodAvailable($shippingMethod), "Shipping rates for {$shippingMethod['shipping_service']} should be $shippingAvaialability." ); } From d357f04f387c81cbcd731443d7ed841b790b4361 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Fri, 18 May 2018 13:53:49 +0300 Subject: [PATCH 28/85] MAGETWO-86125: Sorting on price of configurable products in catalog not working properly --- .../Model/Indexer/ProductPriceIndexFilter.php | 6 +++--- .../_files/product_downloadable_with_files.php | 14 ++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php index a76e98f9b1710..76c6d14057e96 100644 --- a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php +++ b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php @@ -56,21 +56,21 @@ public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = $select = $connection->select(); $select->from( ['price_index' => $priceTable->getTableName()], - '' + [] ); $select->joinLeft( ['website_stock' => $this->stockStatus->getMainTable()], 'website_stock.product_id = price_index.' . $priceTable->getEntityField() . ' AND website_stock.website_id = price_index.' . $priceTable->getWebsiteField() . ' AND website_stock.stock_id = ' . Stock::DEFAULT_STOCK_ID, - '' + [] ); $select->joinLeft( ['default_stock' => $this->stockStatus->getMainTable()], 'default_stock.product_id = price_index.' . $priceTable->getEntityField() . ' AND default_stock.website_id = 0' . ' AND default_stock.stock_id = ' . Stock::DEFAULT_STOCK_ID, - '' + [] ); $stockStatus = $connection->getIfNullSql('website_stock.stock_status', 'default_stock.stock_status'); $select->where($stockStatus . ' = ?', Stock::STOCK_OUT_OF_STOCK); diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php index c6fdcb607e2a8..86aa61a99e1e8 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php @@ -53,12 +53,7 @@ \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH )->setStatus( \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED -)->setStockData([ - 'use_config_manage_stock' => 1, - 'qty' => 100, - 'is_qty_decimal' => 0, - 'is_in_stock' => 1, -]); +); $extension = $product->getExtensionAttributes(); $links = []; @@ -159,3 +154,10 @@ $product->setTypeHasRequiredOptions(false)->setRequiredOptions(false); } $product->save(); + +$stockRegistry = $objectManager->get(\Magento\CatalogInventory\Api\StockRegistryInterface::class); +$stockItem = $stockRegistry->getStockItem($product->getId()); +$stockItem->setUseConfigManageStock(true); +$stockItem->setQty(100); +$stockItem->setIsInStock(true); +$stockRegistry->updateStockItemBySku($product->getSku(), $stockItem); From 6ad9c03bead4c56831f08c1af13f7cd3a53b163a Mon Sep 17 00:00:00 2001 From: mdykas Date: Fri, 18 May 2018 15:40:01 +0200 Subject: [PATCH 29/85] issue/14056 - Coupon API not working for guest user --- app/code/Magento/Quote/Model/CouponManagement.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Quote/Model/CouponManagement.php b/app/code/Magento/Quote/Model/CouponManagement.php index 87398ad36cfab..a46cc344cadb7 100644 --- a/app/code/Magento/Quote/Model/CouponManagement.php +++ b/app/code/Magento/Quote/Model/CouponManagement.php @@ -56,6 +56,9 @@ public function set($cartId, $couponCode) if (!$quote->getItemsCount()) { throw new NoSuchEntityException(__('Cart %1 doesn\'t contain products', $cartId)); } + if (!$quote->getStoreId()) { + throw new NoSuchEntityException(__('Cart isn\'t assigned to correct store')); + } $quote->getShippingAddress()->setCollectShippingRates(true); try { From 788485a3b2e099ead51e1723bf5454718fa06a97 Mon Sep 17 00:00:00 2001 From: Jakub Idziak Date: Fri, 18 May 2018 16:07:40 +0200 Subject: [PATCH 30/85] ISSUE-11477 - fixed Swagger response for searchCriteria - added zero index to array signifier --- app/code/Magento/Webapi/Model/Rest/Swagger/Generator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Webapi/Model/Rest/Swagger/Generator.php b/app/code/Magento/Webapi/Model/Rest/Swagger/Generator.php index 4468a5af6cce1..1e3a9c64e927e 100644 --- a/app/code/Magento/Webapi/Model/Rest/Swagger/Generator.php +++ b/app/code/Magento/Webapi/Model/Rest/Swagger/Generator.php @@ -39,7 +39,7 @@ class Generator extends AbstractSchemaGenerator const UNAUTHORIZED_DESCRIPTION = '401 Unauthorized'; /** Array signifier */ - const ARRAY_SIGNIFIER = '[]'; + const ARRAY_SIGNIFIER = '[0]'; /** * Swagger factory instance. From 161b17a6f3cc7df970206d1bcedcc8e257dc2470 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Wed, 23 May 2018 15:19:54 +0300 Subject: [PATCH 31/85] MAGETWO-86125: Sorting on price of configurable products in catalog not working properly --- .../Magento/Indexer/Model/Indexer/DependencyDecorator.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php b/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php index d0a2732bfaf1d..829df74a1b0ed 100644 --- a/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php +++ b/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php @@ -271,7 +271,10 @@ public function reindexList($ids) $this->indexer->reindexList($ids); $dependentIndexerIds = $this->dependencyInfoProvider->getIndexerIdsToRunAfter($this->indexer->getId()); foreach ($dependentIndexerIds as $indexerId) { - $this->indexerRegistry->get($indexerId)->reindexList($ids); + $dependentIndexer = $this->indexerRegistry->get($indexerId); + if (!$dependentIndexer->isScheduled()) { + $dependentIndexer->reindexList($ids); + } } } } From 8b7bb58d4bd90d426d8166e304801ab04e06fa18 Mon Sep 17 00:00:00 2001 From: mdykas Date: Thu, 24 May 2018 14:49:35 +0200 Subject: [PATCH 32/85] issue/14056 - Coupon API not working for guest user - adjusted unit tests --- .../Quote/Test/Unit/Model/CouponManagementTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/code/Magento/Quote/Test/Unit/Model/CouponManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/CouponManagementTest.php index 27824ed44ef3f..444d7ced635d7 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/CouponManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/CouponManagementTest.php @@ -49,6 +49,7 @@ protected function setUp() 'save', 'getShippingAddress', 'getCouponCode', + 'getStoreId', '__wakeup' ]); $this->quoteAddressMock = $this->createPartialMock(\Magento\Quote\Model\Quote\Address::class, [ @@ -100,6 +101,9 @@ public function testSetWhenCouldNotApplyCoupon() $cartId = 33; $couponCode = '153a-ABC'; + $this->storeMock->expects($this->any())->method('getId')->will($this->returnValue(1)); + $this->quoteMock->expects($this->once())->method('getStoreId')->willReturn($this->returnValue(1)); + $this->quoteRepositoryMock->expects($this->once()) ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); $this->quoteMock->expects($this->once())->method('getItemsCount')->will($this->returnValue(12)); @@ -127,6 +131,9 @@ public function testSetWhenCouponCodeIsInvalid() $cartId = 33; $couponCode = '153a-ABC'; + $this->storeMock->expects($this->any())->method('getId')->will($this->returnValue(1)); + $this->quoteMock->expects($this->once())->method('getStoreId')->willReturn($this->returnValue(1)); + $this->quoteRepositoryMock->expects($this->once()) ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); $this->quoteMock->expects($this->once())->method('getItemsCount')->will($this->returnValue(12)); @@ -146,6 +153,9 @@ public function testSet() $cartId = 33; $couponCode = '153a-ABC'; + $this->storeMock->expects($this->any())->method('getId')->will($this->returnValue(1)); + $this->quoteMock->expects($this->once())->method('getStoreId')->willReturn($this->returnValue(1)); + $this->quoteRepositoryMock->expects($this->once()) ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); $this->quoteMock->expects($this->once())->method('getItemsCount')->will($this->returnValue(12)); From 59e03c1a3d91f8f6c5c81ead729f1ae2e102224d Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta Date: Sat, 26 May 2018 15:56:23 +0200 Subject: [PATCH 33/85] FIX fo rissue #15510 - First PDF download / export after login --- app/code/Magento/Backend/App/AbstractAction.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/code/Magento/Backend/App/AbstractAction.php b/app/code/Magento/Backend/App/AbstractAction.php index 99ee86b2b6407..3f658ee90bf4e 100644 --- a/app/code/Magento/Backend/App/AbstractAction.php +++ b/app/code/Magento/Backend/App/AbstractAction.php @@ -217,6 +217,7 @@ public function dispatch(\Magento\Framework\App\RequestInterface $request) $this->_view->loadLayout(['default', 'adminhtml_denied'], true, true, false); $this->_view->renderLayout(); $this->_request->setDispatched(true); + return $this->_response; } @@ -226,6 +227,11 @@ public function dispatch(\Magento\Framework\App\RequestInterface $request) $this->_processLocaleSettings(); + // Need to preload isFirstPageAfterLogin (see https://github.com/magento/magento2/issues/15510) + if ($this->_auth->isLoggedIn()) { + $this->_auth->getAuthStorage()->isFirstPageAfterLogin(); + } + return parent::dispatch($request); } From 24d7d4afcd9302f2d02b2dba34a60327d32b4224 Mon Sep 17 00:00:00 2001 From: Max Chadwick Date: Sun, 27 May 2018 20:28:39 -0400 Subject: [PATCH 34/85] Fix indentation --- .../Plugin/SeparateAppsTest.php | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/NewRelicReporting/Plugin/SeparateAppsTest.php b/dev/tests/integration/testsuite/Magento/NewRelicReporting/Plugin/SeparateAppsTest.php index 3850a8cb3f9ae..92b0ec0f6a732 100644 --- a/dev/tests/integration/testsuite/Magento/NewRelicReporting/Plugin/SeparateAppsTest.php +++ b/dev/tests/integration/testsuite/Magento/NewRelicReporting/Plugin/SeparateAppsTest.php @@ -12,36 +12,36 @@ class SeparateAppsTest extends \PHPUnit\Framework\TestCase { - /** - * @var ObjectManager - */ - private $objectManager; - - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - } - - /** - * @magentoConfigFixture default/newrelicreporting/general/enable 1 - * @magentoConfigFixture default/newrelicreporting/general/app_name beverly_hills - * @magentoConfigFixture default/newrelicreporting/general/separate_apps 1 - */ - public function testAppNameIsSetWhenConfiguredCorrectly() - { - $newRelicWrapper = $this->getMockBuilder(NewRelicWrapper::class) - ->setMethods(['setAppName']) - ->getMock(); - - $this->objectManager->configure([NewRelicWrapper::class => ['shared' => true]]); - $this->objectManager->addSharedInstance($newRelicWrapper, NewRelicWrapper::class); - - $newRelicWrapper->expects($this->once()) - ->method('setAppName') - ->with($this->equalTo('beverly_hills;beverly_hills_90210')); - - $state = $this->objectManager->get(State::class); - - $state->setAreaCode('90210'); - } + /** + * @var ObjectManager + */ + private $objectManager; + + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + } + + /** + * @magentoConfigFixture default/newrelicreporting/general/enable 1 + * @magentoConfigFixture default/newrelicreporting/general/app_name beverly_hills + * @magentoConfigFixture default/newrelicreporting/general/separate_apps 1 + */ + public function testAppNameIsSetWhenConfiguredCorrectly() + { + $newRelicWrapper = $this->getMockBuilder(NewRelicWrapper::class) + ->setMethods(['setAppName']) + ->getMock(); + + $this->objectManager->configure([NewRelicWrapper::class => ['shared' => true]]); + $this->objectManager->addSharedInstance($newRelicWrapper, NewRelicWrapper::class); + + $newRelicWrapper->expects($this->once()) + ->method('setAppName') + ->with($this->equalTo('beverly_hills;beverly_hills_90210')); + + $state = $this->objectManager->get(State::class); + + $state->setAreaCode('90210'); + } } From 4e1c50c247d48c18e80a27507a2956b0c2ac072a Mon Sep 17 00:00:00 2001 From: Sergey Shvets Date: Tue, 29 May 2018 13:44:32 +0300 Subject: [PATCH 35/85] MAGETWO-91840: Unable to log in when guest checkout is disabled and CAPTCHA enabled --- .../Captcha/Model/Customer/Plugin/AjaxLogin.php | 6 +++--- app/code/Magento/Captcha/Model/DefaultModel.php | 16 ++++++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php b/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php index e496c36f4de75..e1f5e98a111b2 100644 --- a/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php +++ b/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php @@ -81,9 +81,9 @@ public function aroundExecute( if ($content) { $loginParams = $this->serializer->unserialize($content); } - $username = isset($loginParams['username']) ? $loginParams['username'] : null; - $captchaString = isset($loginParams[$captchaInputName]) ? $loginParams[$captchaInputName] : null; - $loginFormId = isset($loginParams[$captchaFormIdField]) ? $loginParams[$captchaFormIdField] : null; + $username = $loginParams['username'] ?? null; + $captchaString = $loginParams[$captchaInputName] ?? null; + $loginFormId = $loginParams[$captchaFormIdField] ?? null; foreach ($this->formIds as $formId) { $captchaModel = $this->helper->getCaptcha($formId); diff --git a/app/code/Magento/Captcha/Model/DefaultModel.php b/app/code/Magento/Captcha/Model/DefaultModel.php index e5c72ba1ae82e..cf6690df5c85d 100644 --- a/app/code/Magento/Captcha/Model/DefaultModel.php +++ b/app/code/Magento/Captcha/Model/DefaultModel.php @@ -5,6 +5,8 @@ */ namespace Magento\Captcha\Model; +use Magento\Captcha\Helper\Data; + /** * Implementation of \Zend\Captcha\Image * @@ -29,7 +31,7 @@ class DefaultModel extends \Zend\Captcha\Image implements \Magento\Captcha\Model const DEFAULT_WORD_LENGTH_TO = 5; /** - * @var \Magento\Captcha\Helper\Data + * @var Data * @since 100.2.0 */ protected $captchaData; @@ -125,8 +127,8 @@ public function getBlockName() */ public function isRequired($login = null) { - if ($this->isUserAuth() - && !$this->isShownToLoggedInUser() + if (($this->isUserAuth() + && !$this->isShownToLoggedInUser()) || !$this->isEnabled() || !in_array( $this->formId, @@ -431,12 +433,14 @@ public function getWordLen() */ private function isShowAlways() { - if ((string)$this->captchaData->getConfig('mode') == \Magento\Captcha\Helper\Data::MODE_ALWAYS) { + $captchaMode = (string)$this->captchaData->getConfig('mode'); + + if ($captchaMode === Data::MODE_ALWAYS) { return true; } - if ((string)$this->captchaData->getConfig('mode') == \Magento\Captcha\Helper\Data::MODE_AFTER_FAIL - && $this->getAllowedAttemptsForSameLogin() == 0 + if ($captchaMode === Data::MODE_AFTER_FAIL + && $this->getAllowedAttemptsForSameLogin() === 0 ) { return true; } From 2a428638fca26083b512565173e17369b889c723 Mon Sep 17 00:00:00 2001 From: Sergey Shvets Date: Tue, 29 May 2018 16:14:04 +0300 Subject: [PATCH 36/85] MAGETWO-91840: Unable to log in when guest checkout is disabled and CAPTCHA enabled --- app/code/Magento/Captcha/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Captcha/etc/di.xml b/app/code/Magento/Captcha/etc/di.xml index 955896eb12744..db1df3c8ff4a4 100644 --- a/app/code/Magento/Captcha/etc/di.xml +++ b/app/code/Magento/Captcha/etc/di.xml @@ -33,7 +33,7 @@ user_login - guest_checkout + From a87d8f148baf5671d114336c6519e1cffa383dea Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi Date: Fri, 25 May 2018 11:04:03 +0300 Subject: [PATCH 37/85] MAGETWO-89971: [B2B] The product total price value on the second website is as on the default website - reverted fix from community PR #11165 - issue #7582 is fixed via plugin on store view switching --- .../Magento/Quote/Model/QuoteRepository.php | 2 +- .../Magento/Quote/Plugin/UpdateQuoteStore.php | 69 ++++++++++ app/code/Magento/Quote/etc/frontend/di.xml | 18 +++ .../Model/Plugin/UpdateQuoteStoreTest.php | 130 ++++++++++++++++++ .../Quote/Model/QuoteRepositoryTest.php | 47 +++++++ 5 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Quote/Plugin/UpdateQuoteStore.php create mode 100644 app/code/Magento/Quote/etc/frontend/di.xml create mode 100644 dev/tests/integration/testsuite/Magento/Quote/Model/Plugin/UpdateQuoteStoreTest.php diff --git a/app/code/Magento/Quote/Model/QuoteRepository.php b/app/code/Magento/Quote/Model/QuoteRepository.php index d3967794b300a..01c21bbbe50a7 100644 --- a/app/code/Magento/Quote/Model/QuoteRepository.php +++ b/app/code/Magento/Quote/Model/QuoteRepository.php @@ -212,7 +212,7 @@ protected function loadQuote($loadMethod, $loadField, $identifier, array $shared if ($sharedStoreIds) { $quote->setSharedStoreIds($sharedStoreIds); } - $quote->$loadMethod($identifier)->setStoreId($this->storeManager->getStore()->getId()); + $quote->setStoreId($this->storeManager->getStore()->getId())->$loadMethod($identifier); if (!$quote->getId()) { throw NoSuchEntityException::singleField($loadField, $identifier); } diff --git a/app/code/Magento/Quote/Plugin/UpdateQuoteStore.php b/app/code/Magento/Quote/Plugin/UpdateQuoteStore.php new file mode 100644 index 0000000000000..de2fc76b9179f --- /dev/null +++ b/app/code/Magento/Quote/Plugin/UpdateQuoteStore.php @@ -0,0 +1,69 @@ +quoteRepository = $quoteRepository; + $this->checkoutSession = $checkoutSession; + } + + /** + * Update store id in active quote after store view switching. + * + * @param StoreCookieManagerInterface $subject + * @param null $result + * @param StoreInterface $store + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterSetStoreCookie( + StoreCookieManagerInterface $subject, + $result, + StoreInterface $store + ) { + $storeCodeFromCookie = $subject->getStoreCodeFromCookie(); + if (null === $storeCodeFromCookie) { + return; + } + + $quote = $this->checkoutSession->getQuote(); + if ($quote->getIsActive() && $store->getCode() != $storeCodeFromCookie) { + $quote->setStoreId( + $store->getId() + ); + $this->quoteRepository->save($quote); + } + } +} diff --git a/app/code/Magento/Quote/etc/frontend/di.xml b/app/code/Magento/Quote/etc/frontend/di.xml new file mode 100644 index 0000000000000..25acd6763ba56 --- /dev/null +++ b/app/code/Magento/Quote/etc/frontend/di.xml @@ -0,0 +1,18 @@ + + + + + + Magento\Quote\Model\QuoteRepository\Proxy + Magento\Checkout\Model\Session\Proxy + + + + + + diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/Plugin/UpdateQuoteStoreTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/Plugin/UpdateQuoteStoreTest.php new file mode 100644 index 0000000000000..cbacab1139c10 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/Plugin/UpdateQuoteStoreTest.php @@ -0,0 +1,130 @@ +objectManager = BootstrapHelper::getObjectManager(); + $this->quoteRepository = $this->objectManager->create(CartRepositoryInterface::class); + } + + /** + * Tests that active quote store id updates after store cookie change. + * + * @magentoDataFixture Magento/Quote/_files/empty_quote.php + * @magentoDataFixture Magento/Store/_files/second_store.php + * @throws \ReflectionException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testUpdateQuoteStoreAfterChangeStoreCookie() + { + $secondStoreCode = 'fixture_second_store'; + $reservedOrderId = 'reserved_order_id'; + + /** @var StoreManagerInterface $storeManager */ + $storeManager = $this->objectManager->get(StoreManagerInterface::class); + $currentStore = $storeManager->getStore(); + + $quote = $this->getQuote($reservedOrderId); + $this->assertEquals( + $currentStore->getId(), + $quote->getStoreId(), + 'Current store id and quote store id are not match' + ); + + /** @var Session $checkoutSession */ + $checkoutSession = $this->objectManager->get(Session::class); + $checkoutSession->setQuoteId($quote->getId()); + + $storeRepository = $this->objectManager->create(StoreRepository::class); + $secondStore = $storeRepository->get($secondStoreCode); + + $storeCookieManager = $this->getStoreCookieManager($currentStore); + $storeCookieManager->setStoreCookie($secondStore); + + $updatedQuote = $this->getQuote($reservedOrderId); + $this->assertEquals( + $secondStore->getId(), + $updatedQuote->getStoreId(), + 'Active quote store id should be equal second store id' + ); + } + + /** + * Retrieves quote by reserved order id. + * + * @param string $reservedOrderId + * @return Quote + */ + private function getQuote(string $reservedOrderId): Quote + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + $items = $this->quoteRepository->getList($searchCriteria)->getItems(); + + return array_pop($items); + } + + /** + * Returns instance of StoreCookieManagerInterface with mocked cookieManager dependency. + * + * Mock is needed since integration test framework use own cookie manager with + * behavior that differs from real environment. + * + * @param $currentStore + * @return StoreCookieManagerInterface + * @throws \ReflectionException + */ + private function getStoreCookieManager(StoreInterface $currentStore): StoreCookieManagerInterface + { + /** @var StoreCookieManagerInterface $storeCookieManager */ + $storeCookieManager = $this->objectManager->get(StoreCookieManagerInterface::class); + $cookieManagerMock = $this->getMockBuilder(\Magento\Framework\Stdlib\Cookie\PhpCookieManager::class) + ->disableOriginalConstructor() + ->getMock(); + $cookieManagerMock->method('getCookie') + ->willReturn($currentStore->getCode()); + + $reflection = new \ReflectionClass($storeCookieManager); + $cookieManager = $reflection->getProperty('cookieManager'); + $cookieManager->setAccessible(true); + $cookieManager->setValue($storeCookieManager, $cookieManagerMock); + + return $storeCookieManager; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php index 88c66fedd10ed..3e093876349d0 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Quote\Model; +use Magento\Store\Model\StoreRepository; use Magento\TestFramework\Helper\Bootstrap as BootstrapHelper; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Api\SearchCriteriaBuilder; @@ -50,6 +51,34 @@ protected function setUp() $this->filterBuilder = $this->objectManager->create(FilterBuilder::class); } + /** + * Tests that quote saved with custom store id has same store id after getting via repository. + * + * @magentoDataFixture Magento/Sales/_files/quote.php + * @magentoDataFixture Magento/Store/_files/second_store.php + */ + public function testGetQuoteWithCustomStoreId() + { + $secondStoreCode = 'fixture_second_store'; + $reservedOrderId = 'test01'; + + $storeRepository = $this->objectManager->create(StoreRepository::class); + $secondStore = $storeRepository->get($secondStoreCode); + + // Set store_id in quote to second store_id + $quote = $this->getQuote($reservedOrderId); + $quote->setStoreId($secondStore->getId()); + $this->quoteRepository->save($quote); + + $savedQuote = $this->quoteRepository->get($quote->getId()); + + $this->assertEquals( + $secondStore->getId(), + $savedQuote->getStoreId(), + 'Quote store id should be equal with store id value in DB' + ); + } + /** * @magentoDataFixture Magento/Sales/_files/quote.php */ @@ -126,6 +155,24 @@ public function testSaveWithNotExistingCustomerAddress() ); } + /** + * Returns quote by reserved order id. + * + * @param string $reservedOrderId + * @return CartInterface + */ + private function getQuote(string $reservedOrderId) + { + $searchCriteria = $this->getSearchCriteria($reservedOrderId); + $searchResult = $this->quoteRepository->getList($searchCriteria); + $items = $searchResult->getItems(); + + /** @var CartInterface $quote */ + $quote = array_pop($items); + + return $quote; + } + /** * Get search criteria * From 1ae4336732eb527b3c4a664e715d93b89c9515ce Mon Sep 17 00:00:00 2001 From: Sergey Shvets Date: Wed, 30 May 2018 17:52:37 +0300 Subject: [PATCH 38/85] MAGETWO-91840: Unable to log in when guest checkout is disabled and CAPTCHA enabled --- app/code/Magento/Captcha/etc/di.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Captcha/etc/di.xml b/app/code/Magento/Captcha/etc/di.xml index db1df3c8ff4a4..3a929f5e6cc00 100644 --- a/app/code/Magento/Captcha/etc/di.xml +++ b/app/code/Magento/Captcha/etc/di.xml @@ -33,7 +33,6 @@ user_login - From 6f74b6ea8dda7b43be26268f1e3d936bccc27361 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Thu, 31 May 2018 14:13:41 +0300 Subject: [PATCH 39/85] MAGETWO-86125: Sorting on price of configurable products in catalog not working properly --- .../Model/Indexer/ProductPriceIndexFilter.php | 44 +++--- .../Model/ResourceModel/Stock.php | 6 + .../Model/ResourceModel/Stock/Item.php | 142 +++++++++++++++++- ...dateItemsStockUponConfigChangeObserver.php | 20 +-- ...ItemsStockUponConfigChangeObserverTest.php | 14 +- 5 files changed, 184 insertions(+), 42 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php index 76c6d14057e96..61291dc24e2e8 100644 --- a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php +++ b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php @@ -8,7 +8,7 @@ namespace Magento\CatalogInventory\Model\Indexer; use Magento\CatalogInventory\Api\StockConfigurationInterface; -use Magento\CatalogInventory\Model\ResourceModel\Stock\Status; +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; @@ -24,18 +24,18 @@ class ProductPriceIndexFilter implements PriceModifierInterface private $stockConfiguration; /** - * @var Status + * @var Item */ - private $stockStatus; + private $stockItem; /** * @param StockConfigurationInterface $stockConfiguration - * @param Status $stockStatus + * @param Item $stockItem */ - public function __construct(StockConfigurationInterface $stockConfiguration, Status $stockStatus) + public function __construct(StockConfigurationInterface $stockConfiguration, Item $stockItem) { $this->stockConfiguration = $stockConfiguration; - $this->stockStatus = $stockStatus; + $this->stockItem = $stockItem; } /** @@ -52,27 +52,31 @@ public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = return; } - $connection = $this->stockStatus->getConnection(); + $connection = $this->stockItem->getConnection(); $select = $connection->select(); $select->from( ['price_index' => $priceTable->getTableName()], [] ); - $select->joinLeft( - ['website_stock' => $this->stockStatus->getMainTable()], - 'website_stock.product_id = price_index.' . $priceTable->getEntityField() - . ' AND website_stock.website_id = price_index.' . $priceTable->getWebsiteField() - . ' AND website_stock.stock_id = ' . Stock::DEFAULT_STOCK_ID, + $select->joinInner( + ['stock_item' => $this->stockItem->getMainTable()], + 'stock_item.product_id = price_index.' . $priceTable->getEntityField() + . ' AND stock_item.stock_id = ' . Stock::DEFAULT_STOCK_ID, [] ); - $select->joinLeft( - ['default_stock' => $this->stockStatus->getMainTable()], - 'default_stock.product_id = price_index.' . $priceTable->getEntityField() - . ' AND default_stock.website_id = 0' - . ' AND default_stock.stock_id = ' . Stock::DEFAULT_STOCK_ID, - [] - ); - $stockStatus = $connection->getIfNullSql('website_stock.stock_status', 'default_stock.stock_status'); + if ($this->stockConfiguration->getManageStock()) { + $stockStatus = $connection->getCheckSql( + 'use_config_manage_stock = 0 AND manage_stock = 0', + 1, + 'is_in_stock' + ); + } else { + $stockStatus = $connection->getCheckSql( + 'use_config_manage_stock = 0 AND manage_stock = 1', + 'is_in_stock', + 1 + ); + } $select->where($stockStatus . ' = ?', Stock::STOCK_OUT_OF_STOCK); $query = $select->deleteFromSelect('price_index'); diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php index 4a39ac2868046..53f00529b9bcc 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php @@ -206,6 +206,8 @@ protected function _initConfig() /** * Set items out of stock basing on their quantities and config settings * + * @deprecated + * @see \Magento\CatalogInventory\Model\ResourceModel\Stock\Item::updateSetOutOfStock * @param string|int $website * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @return void @@ -241,6 +243,8 @@ public function updateSetOutOfStock($website = null) /** * Set items in stock basing on their quantities and config settings * + * @deprecated + * @see \Magento\CatalogInventory\Model\ResourceModel\Stock\Item::updateSetInStock * @param int|string $website * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @return void @@ -274,6 +278,8 @@ public function updateSetInStock($website) /** * Update items low stock date basing on their quantities and config settings * + * @deprecated + * @see \Magento\CatalogInventory\Model\ResourceModel\Stock\Item::updateLowStockDate * @param int|string $website * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @return void diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php index 895fffaa4f80b..92f1e67c61981 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php @@ -6,10 +6,14 @@ namespace Magento\CatalogInventory\Model\ResourceModel\Stock; use Magento\CatalogInventory\Api\Data\StockItemInterface; +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\CatalogInventory\Model\Stock; use Magento\CatalogInventory\Model\Indexer\Stock\Processor; -use Magento\Framework\App\ResourceConnection as AppResource; use Magento\Framework\Model\AbstractModel; -use Magento\Framework\Model\ResourceModel\Db\TransactionManagerInterface; +use Magento\Framework\Model\ResourceModel\Db\Context; +use Magento\Framework\DB\Select; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Stdlib\DateTime\DateTime; /** * Stock item resource model @@ -29,17 +33,36 @@ class Item extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb protected $stockIndexerProcessor; /** - * @param \Magento\Framework\Model\ResourceModel\Db\Context $context + * @var StockConfigurationInterface + */ + private $stockConfiguration; + + /** + * @var DateTime + */ + private $dateTime; + + /** + * @param Context $context * @param Processor $processor * @param string $connectionName + * @param StockConfigurationInterface $stockConfiguration + * @param DateTime $dateTime */ public function __construct( - \Magento\Framework\Model\ResourceModel\Db\Context $context, + Context $context, Processor $processor, - $connectionName = null + $connectionName = null, + StockConfigurationInterface $stockConfiguration = null, + DateTime $dateTime = null ) { $this->stockIndexerProcessor = $processor; parent::__construct($context, $connectionName); + + $this->stockConfiguration = $stockConfiguration ?? + ObjectManager::getInstance()->get(StockConfigurationInterface::class); + $this->dateTime = $dateTime ?? + ObjectManager::getInstance()->get(DateTime::class); } /** @@ -139,4 +162,113 @@ public function setProcessIndexEvents($process = true) $this->processIndexEvents = $process; return $this; } + + /** + * Set items out of stock basing on their quantities and config settings + * + * @param int $websiteId + * @return void + */ + public function updateSetOutOfStock(int $websiteId) + { + $connection = $this->getConnection(); + + $values = [ + 'is_in_stock' => Stock::STOCK_OUT_OF_STOCK, + 'stock_status_changed_auto' => 1, + ]; + $select = $this->buildProductsSelectByConfigTypes(); + $where = [ + 'website_id = ' . $websiteId, + 'is_in_stock = ' . Stock::STOCK_IN_STOCK, + '(use_config_manage_stock = 1 AND 1 = ' . $this->stockConfiguration->getManageStock() . ')' + . ' OR (use_config_manage_stock = 0 AND manage_stock = 1)', + '(use_config_min_qty = 1 AND qty <= ' . $this->stockConfiguration->getMinQty() . ')' + . ' OR (use_config_min_qty = 0 AND qty <= min_qty)', + 'product_id IN (' . $select->assemble() . ')', + ]; + $backordersWhere = '(use_config_backorders = 0 AND backorders = ' . Stock::BACKORDERS_NO . ')'; + if (Stock::BACKORDERS_NO == $this->stockConfiguration->getBackorders()) { + $where[] = $backordersWhere . ' OR use_config_backorders = 1'; + } else { + $where[] = $backordersWhere; + } + $connection->update($this->getMainTable(), $values, $where); + + $this->stockIndexerProcessor->markIndexerAsInvalid(); + } + + /** + * Set items in stock basing on their quantities and config settings + * + * @param int $websiteId + * @return void + */ + public function updateSetInStock(int $websiteId) + { + $connection = $this->getConnection(); + + $values = [ + 'is_in_stock' => Stock::STOCK_IN_STOCK, + ]; + $select = $this->buildProductsSelectByConfigTypes(); + $where = [ + 'website_id = ' . $websiteId, + 'stock_status_changed_auto = 1', + '(use_config_min_qty = 1 AND qty > ' . $this->stockConfiguration->getMinQty() . ')' + . ' OR (use_config_min_qty = 0 AND qty > min_qty)', + 'product_id IN (' . $select->assemble() . ')', + ]; + $manageStockWhere = '(use_config_manage_stock = 0 AND manage_stock = 1)'; + if ($this->stockConfiguration->getManageStock()) { + $where[] = $manageStockWhere . ' OR use_config_manage_stock = 1'; + } else { + $where[] = $manageStockWhere; + } + $connection->update($this->getMainTable(), $values, $where); + + $this->stockIndexerProcessor->markIndexerAsInvalid(); + } + + /** + * Update items low stock date basing on their quantities and config settings + * + * @param int $websiteId + * @return void + */ + public function updateLowStockDate(int $websiteId) + { + $connection = $this->getConnection(); + + $condition = $connection->quoteInto( + '(use_config_notify_stock_qty = 1 AND qty < ?)', + $this->stockConfiguration->getNotifyStockQty() + ) . ' OR (use_config_notify_stock_qty = 0 AND qty < notify_stock_qty)'; + $currentDbTime = $connection->quoteInto('?', $this->dateTime->gmtDate()); + $conditionalDate = $connection->getCheckSql($condition, $currentDbTime, 'NULL'); + $value = [ + 'low_stock_date' => new \Zend_Db_Expr($conditionalDate), + ]; + $select = $this->buildProductsSelectByConfigTypes(); + $where = [ + 'website_id = ' . $websiteId, + 'product_id IN (' . $select->assemble() . ')' + ]; + $manageStockWhere = '(use_config_manage_stock = 0 AND manage_stock = 1)'; + if ($this->stockConfiguration->getManageStock()) { + $where[] = $manageStockWhere . ' OR use_config_manage_stock = 1'; + } else { + $where[] = $manageStockWhere; + } + $connection->update($this->getMainTable(), $value, $where); + } + + private function buildProductsSelectByConfigTypes(): Select + { + $select = $this->getConnection()->select() + ->from($this->getTable('catalog_product_entity'), 'entity_id') + ->where('type_id IN (?)', array_keys($this->stockConfiguration->getIsQtyTypeIds(true))); + + return $select; + } } diff --git a/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php b/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php index 47ee6512920d8..c0d0a589b925d 100644 --- a/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php +++ b/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php @@ -3,11 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\CatalogInventory\Observer; use Magento\Framework\Event\Observer as EventObserver; use Magento\Framework\Event\ObserverInterface; +use Magento\CatalogInventory\Model\ResourceModel\Stock\Item; /** * Catalog inventory module observer @@ -15,16 +15,16 @@ class UpdateItemsStockUponConfigChangeObserver implements ObserverInterface { /** - * @var \Magento\CatalogInventory\Model\ResourceModel\Stock + * @var Item */ - protected $resourceStock; + protected $resourceStockItem; /** - * @param \Magento\CatalogInventory\Model\ResourceModel\Stock $resourceStock + * @param Item $resourceStockItem */ - public function __construct(\Magento\CatalogInventory\Model\ResourceModel\Stock $resourceStock) + public function __construct(Item $resourceStockItem) { - $this->resourceStock = $resourceStock; + $this->resourceStockItem = $resourceStockItem; } /** @@ -35,9 +35,9 @@ public function __construct(\Magento\CatalogInventory\Model\ResourceModel\Stock */ public function execute(EventObserver $observer) { - $website = $observer->getEvent()->getWebsite(); - $this->resourceStock->updateSetOutOfStock($website); - $this->resourceStock->updateSetInStock($website); - $this->resourceStock->updateLowStockDate($website); + $website = (int) $observer->getEvent()->getWebsite(); + $this->resourceStockItem->updateSetOutOfStock($website); + $this->resourceStockItem->updateSetInStock($website); + $this->resourceStockItem->updateLowStockDate($website); } } diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php index 70a179b484379..0e81cdf6b9123 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php @@ -15,9 +15,9 @@ class UpdateItemsStockUponConfigChangeObserverTest extends \PHPUnit\Framework\Te protected $observer; /** - * @var \Magento\CatalogInventory\Model\ResourceModel\Stock|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\CatalogInventory\Model\ResourceModel\Stock\Item|\PHPUnit_Framework_MockObject_MockObject */ - protected $resourceStock; + protected $resourceStockItem; /** * @var \Magento\Framework\Event|\PHPUnit_Framework_MockObject_MockObject @@ -31,7 +31,7 @@ class UpdateItemsStockUponConfigChangeObserverTest extends \PHPUnit\Framework\Te protected function setUp() { - $this->resourceStock = $this->createMock(\Magento\CatalogInventory\Model\ResourceModel\Stock::class); + $this->resourceStockItem = $this->createMock(\Magento\CatalogInventory\Model\ResourceModel\Stock\Item::class); $this->event = $this->getMockBuilder(\Magento\Framework\Event::class) ->disableOriginalConstructor() @@ -50,7 +50,7 @@ protected function setUp() $this->observer = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject( \Magento\CatalogInventory\Observer\UpdateItemsStockUponConfigChangeObserver::class, [ - 'resourceStock' => $this->resourceStock, + 'resourceStockItem' => $this->resourceStockItem, ] ); } @@ -58,9 +58,9 @@ protected function setUp() public function testUpdateItemsStockUponConfigChange() { $websiteId = 1; - $this->resourceStock->expects($this->once())->method('updateSetOutOfStock'); - $this->resourceStock->expects($this->once())->method('updateSetInStock'); - $this->resourceStock->expects($this->once())->method('updateLowStockDate'); + $this->resourceStockItem->expects($this->once())->method('updateSetOutOfStock'); + $this->resourceStockItem->expects($this->once())->method('updateSetInStock'); + $this->resourceStockItem->expects($this->once())->method('updateLowStockDate'); $this->event->expects($this->once()) ->method('getWebsite') From e503bce1db054f218aa4b80080cbe29400eed1f9 Mon Sep 17 00:00:00 2001 From: Sergey Shvets Date: Thu, 31 May 2018 15:16:27 +0300 Subject: [PATCH 40/85] MAGETWO-91840: Unable to log in when guest checkout is disabled and CAPTCHA enabled --- .../Model/Customer/Plugin/AjaxLogin.php | 42 ++++++++++++++----- app/code/Magento/Captcha/etc/di.xml | 1 + 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php b/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php index e1f5e98a111b2..9dc5ed8398888 100644 --- a/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php +++ b/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Captcha\Model\Customer\Plugin; use Magento\Captcha\Helper\Data as CaptchaHelper; @@ -85,23 +86,44 @@ public function aroundExecute( $captchaString = $loginParams[$captchaInputName] ?? null; $loginFormId = $loginParams[$captchaFormIdField] ?? null; - foreach ($this->formIds as $formId) { - $captchaModel = $this->helper->getCaptcha($formId); - if ($captchaModel->isRequired($username) && !in_array($loginFormId, $this->formIds)) { - $resultJson = $this->resultJsonFactory->create(); - return $resultJson->setData(['errors' => true, 'message' => __('Provided form does not exist')]); - } + if (!$this->isFormCaptchaExist($loginFormId, $username)) { + return $this->returnJsonError(__('Provided form does not exist')); + } - if ($formId == $loginFormId) { + foreach ($this->formIds as $formId) { + if ($formId === $loginFormId) { + $captchaModel = $this->helper->getCaptcha($formId); $captchaModel->logAttempt($username); if (!$captchaModel->isCorrect($captchaString)) { $this->sessionManager->setUsername($username); - /** @var \Magento\Framework\Controller\Result\Json $resultJson */ - $resultJson = $this->resultJsonFactory->create(); - return $resultJson->setData(['errors' => true, 'message' => __('Incorrect CAPTCHA')]); + return $this->returnJsonError(__('Incorrect CAPTCHA')); } } } return $proceed(); } + + /** + * + * @param \Magento\Framework\Phrase $phrase + * @return \Magento\Framework\Controller\Result\Json + */ + private function returnJsonError(\Magento\Framework\Phrase $phrase): \Magento\Framework\Controller\Result\Json + { + $resultJson = $this->resultJsonFactory->create(); + return $resultJson->setData(['errors' => true, 'message' => $phrase]); + } + + /** + * @param string $loginFormId + * @param string $username + * @return bool + */ + private function isFormCaptchaExist($loginFormId, $username): bool + { + return ( + $this->helper->getCaptcha($loginFormId)->isRequired($username) + && !in_array($loginFormId, $this->formIds) + ); + } } diff --git a/app/code/Magento/Captcha/etc/di.xml b/app/code/Magento/Captcha/etc/di.xml index 3a929f5e6cc00..955896eb12744 100644 --- a/app/code/Magento/Captcha/etc/di.xml +++ b/app/code/Magento/Captcha/etc/di.xml @@ -33,6 +33,7 @@ user_login + guest_checkout From 7b967146e0793a30f8bd970809121689e65892f1 Mon Sep 17 00:00:00 2001 From: Sergey Shvets Date: Thu, 31 May 2018 16:40:25 +0300 Subject: [PATCH 41/85] MAGETWO-91840: Unable to log in when guest checkout is disabled and CAPTCHA enabled --- app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php b/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php index 9dc5ed8398888..70856336720e5 100644 --- a/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php +++ b/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php @@ -122,8 +122,8 @@ private function returnJsonError(\Magento\Framework\Phrase $phrase): \Magento\Fr private function isFormCaptchaExist($loginFormId, $username): bool { return ( - $this->helper->getCaptcha($loginFormId)->isRequired($username) - && !in_array($loginFormId, $this->formIds) + in_array($loginFormId, $this->formIds) + && $this->helper->getCaptcha($loginFormId)->isRequired($username) ); } } From d16e29cc5bffda9accb4ffdc92e03f111815afac Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi Date: Thu, 31 May 2018 18:17:52 +0300 Subject: [PATCH 42/85] MAGETWO-89220: Cart rule using subselection condition incorrectly gives discount --- app/code/Magento/Quote/Model/Quote.php | 2 +- .../Model/Rule/Condition/ProductTest.php | 75 +++++++++++++++++++ .../_files/cart_rule_10_percent_off.php | 64 ++++++++++++++++ 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_10_percent_off.php diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index 536c58861a253..dca9aa36fb25d 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -1400,7 +1400,7 @@ public function getAllVisibleItems() { $items = []; foreach ($this->getItemsCollection() as $item) { - if (!$item->isDeleted() && !$item->getParentItemId()) { + if (!$item->isDeleted() && !$item->getParentItemId() && !$item->getParentItem()) { $items[] = $item; } } diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php index fb07246656693..b31e77c0b09eb 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php @@ -6,6 +6,14 @@ namespace Magento\SalesRule\Model\Rule\Condition; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Quote\Api\Data\CartInterface; +use Magento\SalesRule\Api\RuleRepositoryInterface; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class ProductTest extends \PHPUnit\Framework\TestCase { /** @@ -94,4 +102,71 @@ public function validateProductConditionDataProvider() ] ]; } + + /** + * Ensure that SalesRules filtering on quote items quantity validates configurable product correctly + * + * 1. Load a quote with a configured product and a sales rule set to filter items with quantity 2. + * 2. Attempt to validate the sales rule against the quote and assert the output is negative. + * + * @magentoDataFixture Magento/ConfigurableProduct/_files/quote_with_configurable_product.php + * @magentoDataFixture Magento/SalesRule/_files/cart_rule_10_percent_off.php + */ + public function testValidateQtySalesRuleWithConfigurable() + { + // Load the quote that contains a child of a configurable product with quantity 1. + $quote = $this->getQuote('test_cart_with_configurable'); + + // Load the SalesRule looking for products with quantity 2. + $rule = $this->getSalesRule('10% Off on orders with two items'); + + $this->assertFalse( + $rule->validate($quote->getBillingAddress()) + ); + } + + /** + * Gets quote by reserved order id. + * + * @param string $reservedOrderId + * @return CartInterface + */ + private function getQuote($reservedOrderId) + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); + $items = $quoteRepository->getList($searchCriteria)->getItems(); + return array_pop($items); + } + + /** + * Gets rule by name. + * + * @param string $name + * @return \Magento\SalesRule\Model\Rule + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getSalesRule(string $name): \Magento\SalesRule\Model\Rule + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('name', $name) + ->create(); + + /** @var CartRepositoryInterface $quoteRepository */ + $ruleRepository = $this->objectManager->get(RuleRepositoryInterface::class); + $items = $ruleRepository->getList($searchCriteria)->getItems(); + + $rule = array_pop($items); + /** @var \Magento\SalesRule\Model\Converter\ToModel $converter */ + $converter = $this->objectManager->get(\Magento\SalesRule\Model\Converter\ToModel::class); + + return $converter->toModel($rule); + } } diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_10_percent_off.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_10_percent_off.php new file mode 100644 index 0000000000000..0761e5753da75 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_10_percent_off.php @@ -0,0 +1,64 @@ +get(StoreManagerInterface::class) + ->getWebsite() + ->getId(); + +/** @var Rule $salesRule */ +$salesRule = $objectManager->create(Rule::class); +$salesRule->setData( + [ + 'name' => '10% Off on orders with two items', + 'is_active' => 1, + 'customer_group_ids' => [GroupManagement::NOT_LOGGED_IN_ID], + 'coupon_type' => Rule::COUPON_TYPE_NO_COUPON, + 'simple_action' => 'by_percent', + 'discount_amount' => 10, + 'discount_step' => 0, + 'stop_rules_processing' => 1, + 'website_ids' => [$websiteId] + ] +); + +$salesRule->getConditions()->loadArray([ + 'type' => \Magento\SalesRule\Model\Rule\Condition\Combine::class, + 'attribute' => null, + 'operator' => null, + 'value' => '1', + 'is_value_processed' => null, + 'aggregator' => 'all', + 'conditions' => + [ + [ + 'type' => \Magento\SalesRule\Model\Rule\Condition\Product\Subselect::class, + 'attribute' => 'qty', + 'operator' => '==', + 'value' => '2', + 'is_value_processed' => null, + 'aggregator' => 'all', + 'conditions' => + [ + [ + 'type' => \Magento\SalesRule\Model\Rule\Condition\Product::class, + 'attribute' => 'attribute_set_id', + 'operator' => '==', + 'value' => '4', + 'is_value_processed' => false, + ], + ], + ], + ], +]); + +$salesRule->save(); From c25c648f7862bfae19c021cac6689709bd276a42 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Thu, 31 May 2018 16:31:58 +0300 Subject: [PATCH 43/85] MAGETWO-86125: Sorting on price of configurable products in catalog not working properly --- .../Model/Indexer/ProductPriceIndexFilter.php | 4 +- .../Model/Plugin/PriceIndexUpdater.php | 77 +++++++++++++++++++ ...datePriceIndexUponConfigChangeObserver.php | 9 ++- ...dateItemsStockUponConfigChangeObserver.php | 16 +++- app/code/Magento/CatalogInventory/etc/di.xml | 3 + .../Magento/CatalogInventory/etc/indexer.xml | 5 -- app/code/Magento/Config/Model/Config.php | 21 ++++- 7 files changed, 119 insertions(+), 16 deletions(-) create mode 100644 app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php index 61291dc24e2e8..51e31c71ba362 100644 --- a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php +++ b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php @@ -67,14 +67,14 @@ public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = if ($this->stockConfiguration->getManageStock()) { $stockStatus = $connection->getCheckSql( 'use_config_manage_stock = 0 AND manage_stock = 0', - 1, + Stock::STOCK_IN_STOCK, 'is_in_stock' ); } else { $stockStatus = $connection->getCheckSql( 'use_config_manage_stock = 0 AND manage_stock = 1', 'is_in_stock', - 1 + Stock::STOCK_IN_STOCK ); } $select->where($stockStatus . ' = ?', Stock::STOCK_OUT_OF_STOCK); diff --git a/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php b/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php new file mode 100644 index 0000000000000..2ed78b29f51cd --- /dev/null +++ b/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php @@ -0,0 +1,77 @@ +priceIndexProcessor = $priceIndexProcessor; + } + + /** + * @param ItemResourceModel $subject + * @param ItemResourceModel $result + * @param ItemModel $model + * @return ItemResourceModel + * SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterSave(ItemResourceModel $subject, ItemResourceModel $result, ItemModel $model) + { + $fields = [ + 'is_in_stock', + 'use_config_manage_stock', + 'manage_stock', + ]; + foreach ($fields as $field) { + if ($model->dataHasChangedFor($field)) { + $this->priceIndexProcessor->reindexRow($model->getProductId()); + break; + } + } + + return $result; + } + + /** + * @param ItemResourceModel $subject + * @param $result + * @param int $websiteId + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterUpdateSetOutOfStock(ItemResourceModel $subject, $result, int $websiteId) + { + $this->priceIndexProcessor->markIndexerAsInvalid(); + } + + /** + * @param ItemResourceModel $subject + * @param $result + * @param int $websiteId + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterUpdateSetInStock(ItemResourceModel $subject, $result, int $websiteId) + { + $this->priceIndexProcessor->markIndexerAsInvalid(); + } +} diff --git a/app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php b/app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php index 78b4cdfe26988..976110ec76cf3 100644 --- a/app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php +++ b/app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php @@ -9,6 +9,7 @@ use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Event\Observer; +use Magento\CatalogInventory\Model\Configuration; use Magento\Catalog\Model\Indexer\Product\Price\Processor; /** @@ -38,7 +39,11 @@ public function __construct(Processor $priceIndexProcessor) */ public function execute(Observer $observer) { - $priceIndexer = $this->priceIndexProcessor->getIndexer(); - $priceIndexer->invalidate(); + $changedPaths = (array) $observer->getEvent()->getChangedPaths(); + + if (\in_array(Configuration::XML_PATH_SHOW_OUT_OF_STOCK, $changedPaths, true)) { + $priceIndexer = $this->priceIndexProcessor->getIndexer(); + $priceIndexer->invalidate(); + } } } diff --git a/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php b/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php index c0d0a589b925d..21c78eca93695 100644 --- a/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php +++ b/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php @@ -7,6 +7,7 @@ use Magento\Framework\Event\Observer as EventObserver; use Magento\Framework\Event\ObserverInterface; +use Magento\CatalogInventory\Model\Configuration; use Magento\CatalogInventory\Model\ResourceModel\Stock\Item; /** @@ -36,8 +37,17 @@ public function __construct(Item $resourceStockItem) public function execute(EventObserver $observer) { $website = (int) $observer->getEvent()->getWebsite(); - $this->resourceStockItem->updateSetOutOfStock($website); - $this->resourceStockItem->updateSetInStock($website); - $this->resourceStockItem->updateLowStockDate($website); + $changedPaths = (array) $observer->getEvent()->getChangedPaths(); + + if (\array_intersect([ + Configuration::XML_PATH_MANAGE_STOCK, + Configuration::XML_PATH_MIN_QTY, + Configuration::XML_PATH_BACKORDERS, + Configuration::XML_PATH_NOTIFY_STOCK_QTY, + ], $changedPaths)) { + $this->resourceStockItem->updateSetOutOfStock($website); + $this->resourceStockItem->updateSetInStock($website); + $this->resourceStockItem->updateLowStockDate($website); + } } } diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml index 0cdf25e90ce63..264f25d24647e 100644 --- a/app/code/Magento/CatalogInventory/etc/di.xml +++ b/app/code/Magento/CatalogInventory/etc/di.xml @@ -125,4 +125,7 @@ + + + diff --git a/app/code/Magento/CatalogInventory/etc/indexer.xml b/app/code/Magento/CatalogInventory/etc/indexer.xml index af23d79bc2925..725477f64908f 100644 --- a/app/code/Magento/CatalogInventory/etc/indexer.xml +++ b/app/code/Magento/CatalogInventory/etc/indexer.xml @@ -10,9 +10,4 @@ Stock Index stock - - - - - diff --git a/app/code/Magento/Config/Model/Config.php b/app/code/Magento/Config/Model/Config.php index bc1515aadb0ca..28e58db268b77 100644 --- a/app/code/Magento/Config/Model/Config.php +++ b/app/code/Magento/Config/Model/Config.php @@ -131,6 +131,7 @@ public function save() $saveTransaction = $this->_transactionFactory->create(); /* @var $saveTransaction \Magento\Framework\DB\Transaction */ + $changedPaths = []; // Extends for old config data $extraOldGroups = []; @@ -143,7 +144,8 @@ public function save() $extraOldGroups, $oldConfig, $saveTransaction, - $deleteTransaction + $deleteTransaction, + $changedPaths ); } @@ -157,7 +159,11 @@ public function save() // website and store codes can be used in event implementation, so set them as well $this->_eventManager->dispatch( "admin_system_config_changed_section_{$this->getSection()}", - ['website' => $this->getWebsite(), 'store' => $this->getStore()] + [ + 'website' => $this->getWebsite(), + 'store' => $this->getStore(), + 'changed_paths' => $changedPaths, + ] ); } catch (\Exception $e) { // re-init configuration @@ -179,6 +185,7 @@ public function save() * @param array &$oldConfig * @param \Magento\Framework\DB\Transaction $saveTransaction * @param \Magento\Framework\DB\Transaction $deleteTransaction + * @param array &$changedPaths * @return void * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -192,7 +199,8 @@ protected function _processGroup( array &$extraOldGroups, array &$oldConfig, \Magento\Framework\DB\Transaction $saveTransaction, - \Magento\Framework\DB\Transaction $deleteTransaction + \Magento\Framework\DB\Transaction $deleteTransaction, + array &$changedPaths = [] ) { $groupPath = $sectionPath . '/' . $groupId; $scope = $this->getScope(); @@ -292,9 +300,13 @@ protected function _processGroup( } else { $deleteTransaction->addObject($backendModel); } + if ($oldConfig[$path]['value'] !== $fieldData['value']) { + $changedPaths[] = $path; + } } elseif (!$inherit) { $backendModel->unsConfigId(); $saveTransaction->addObject($backendModel); + $changedPaths[] = $path; } } } @@ -309,7 +321,8 @@ protected function _processGroup( $extraOldGroups, $oldConfig, $saveTransaction, - $deleteTransaction + $deleteTransaction, + $changedPaths ); } } From 312e47cc75cbd6c2c4e168b431766257b32aba93 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Fri, 1 Jun 2018 14:08:38 +0300 Subject: [PATCH 44/85] MAGETWO-86125: Sorting on price of configurable products in catalog not working properly --- .../Model/Plugin/PriceIndexUpdater.php | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php b/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php index 2ed78b29f51cd..4aa002fffb938 100644 --- a/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php +++ b/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php @@ -5,9 +5,9 @@ */ namespace Magento\CatalogInventory\Model\Plugin; -use Magento\CatalogInventory\Model\Stock\Item as ItemModel; -use Magento\CatalogInventory\Model\ResourceModel\Stock\Item as ItemResourceModel; +use Magento\CatalogInventory\Model\ResourceModel\Stock\Item; use Magento\Catalog\Model\Indexer\Product\Price\Processor; +use Magento\Framework\Model\AbstractModel; /** * Update product price index after product stock status changed. @@ -28,13 +28,13 @@ public function __construct(Processor $priceIndexProcessor) } /** - * @param ItemResourceModel $subject - * @param ItemResourceModel $result - * @param ItemModel $model - * @return ItemResourceModel + * @param Item $subject + * @param Item $result + * @param AbstractModel $model + * @return Item * SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterSave(ItemResourceModel $subject, ItemResourceModel $result, ItemModel $model) + public function afterSave(Item $subject, Item $result, AbstractModel $model) { $fields = [ 'is_in_stock', @@ -52,25 +52,25 @@ public function afterSave(ItemResourceModel $subject, ItemResourceModel $result, } /** - * @param ItemResourceModel $subject + * @param Item $subject * @param $result * @param int $websiteId * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterUpdateSetOutOfStock(ItemResourceModel $subject, $result, int $websiteId) + public function afterUpdateSetOutOfStock(Item $subject, $result, int $websiteId) { $this->priceIndexProcessor->markIndexerAsInvalid(); } /** - * @param ItemResourceModel $subject + * @param Item $subject * @param $result * @param int $websiteId * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterUpdateSetInStock(ItemResourceModel $subject, $result, int $websiteId) + public function afterUpdateSetInStock(Item $subject, $result, int $websiteId) { $this->priceIndexProcessor->markIndexerAsInvalid(); } From 169b45e009f0fe3fff0c5f54945ea282e468e3bf Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Fri, 1 Jun 2018 14:17:50 +0300 Subject: [PATCH 45/85] MAGETWO-86125: Sorting on price of configurable products in catalog not working properly --- .../Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php b/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php index 4aa002fffb938..316f7000ae4af 100644 --- a/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php +++ b/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php @@ -32,7 +32,7 @@ public function __construct(Processor $priceIndexProcessor) * @param Item $result * @param AbstractModel $model * @return Item - * SuppressWarnings(PHPMD.UnusedFormalParameter) + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterSave(Item $subject, Item $result, AbstractModel $model) { From 697a5fd9e9e093a6cd82da30efd3bad001a12fec Mon Sep 17 00:00:00 2001 From: Sergey Shvets Date: Fri, 1 Jun 2018 14:21:34 +0300 Subject: [PATCH 46/85] MAGETWO-91840: Unable to log in when guest checkout is disabled and CAPTCHA enabled --- .../Model/Customer/Plugin/AjaxLogin.php | 25 +++------ .../Model/Customer/Plugin/AjaxLoginTest.php | 52 +++++++++++++------ 2 files changed, 43 insertions(+), 34 deletions(-) diff --git a/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php b/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php index 70856336720e5..91f3a785df36b 100644 --- a/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php +++ b/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php @@ -86,17 +86,19 @@ public function aroundExecute( $captchaString = $loginParams[$captchaInputName] ?? null; $loginFormId = $loginParams[$captchaFormIdField] ?? null; - if (!$this->isFormCaptchaExist($loginFormId, $username)) { + if (!in_array($loginFormId, $this->formIds) && $this->helper->getCaptcha($loginFormId)->isRequired($username)) { return $this->returnJsonError(__('Provided form does not exist')); } foreach ($this->formIds as $formId) { if ($formId === $loginFormId) { $captchaModel = $this->helper->getCaptcha($formId); - $captchaModel->logAttempt($username); - if (!$captchaModel->isCorrect($captchaString)) { - $this->sessionManager->setUsername($username); - return $this->returnJsonError(__('Incorrect CAPTCHA')); + if ($captchaModel->isRequired($username)) { + $captchaModel->logAttempt($username); + if (!$captchaModel->isCorrect($captchaString)) { + $this->sessionManager->setUsername($username); + return $this->returnJsonError(__('Incorrect CAPTCHA')); + } } } } @@ -113,17 +115,4 @@ private function returnJsonError(\Magento\Framework\Phrase $phrase): \Magento\Fr $resultJson = $this->resultJsonFactory->create(); return $resultJson->setData(['errors' => true, 'message' => $phrase]); } - - /** - * @param string $loginFormId - * @param string $username - * @return bool - */ - private function isFormCaptchaExist($loginFormId, $username): bool - { - return ( - in_array($loginFormId, $this->formIds) - && $this->helper->getCaptcha($loginFormId)->isRequired($username) - ); - } } diff --git a/app/code/Magento/Captcha/Test/Unit/Model/Customer/Plugin/AjaxLoginTest.php b/app/code/Magento/Captcha/Test/Unit/Model/Customer/Plugin/AjaxLoginTest.php index be9574fff2cfa..9c0d6f86cd028 100644 --- a/app/code/Magento/Captcha/Test/Unit/Model/Customer/Plugin/AjaxLoginTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Model/Customer/Plugin/AjaxLoginTest.php @@ -3,22 +3,23 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Captcha\Test\Unit\Model\Customer\Plugin; class AjaxLoginTest extends \PHPUnit\Framework\TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Checkout\Model\Session */ protected $sessionManagerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Captcha\Helper\Data */ protected $captchaHelperMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Controller\Result\JsonFactory */ protected $jsonFactoryMock; @@ -38,12 +39,12 @@ class AjaxLoginTest extends \PHPUnit\Framework\TestCase protected $requestMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Customer\Controller\Ajax\Login */ protected $loginControllerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Serialize\Serializer\Json */ protected $serializerMock; @@ -72,8 +73,12 @@ protected function setUp() $this->loginControllerMock->expects($this->any())->method('getRequest') ->will($this->returnValue($this->requestMock)); - $this->captchaHelperMock->expects($this->once())->method('getCaptcha') - ->with('user_login')->will($this->returnValue($this->captchaMock)); + + $this->captchaHelperMock + ->expects($this->exactly(1)) + ->method('getCaptcha') + ->will($this->returnValue($this->captchaMock)); + $this->formIds = ['user_login']; $this->serializerMock = $this->createMock(\Magento\Framework\Serialize\Serializer\Json::class); @@ -103,11 +108,18 @@ public function testAroundExecute() $this->captchaMock->expects($this->once())->method('logAttempt')->with($username); $this->captchaMock->expects($this->once())->method('isCorrect')->with($captchaString) ->will($this->returnValue(true)); - $this->serializerMock->expects(($this->once()))->method('unserialize')->will($this->returnValue($requestData)); + $this->serializerMock->expects($this->once())->method('unserialize')->will($this->returnValue($requestData)); $closure = function () { return 'result'; }; + + $this->captchaHelperMock + ->expects($this->exactly(1)) + ->method('getCaptcha') + ->with('user_login') + ->will($this->returnValue($this->captchaMock)); + $this->assertEquals('result', $this->model->aroundExecute($this->loginControllerMock, $closure)); } @@ -128,18 +140,21 @@ public function testAroundExecuteIncorrectCaptcha() $this->captchaMock->expects($this->once())->method('logAttempt')->with($username); $this->captchaMock->expects($this->once())->method('isCorrect') ->with($captchaString)->will($this->returnValue(false)); - $this->serializerMock->expects(($this->once()))->method('unserialize')->will($this->returnValue($requestData)); + $this->serializerMock->expects($this->once())->method('unserialize')->will($this->returnValue($requestData)); $this->sessionManagerMock->expects($this->once())->method('setUsername')->with($username); $this->jsonFactoryMock->expects($this->once())->method('create') ->will($this->returnValue($this->resultJsonMock)); - $this->resultJsonMock->expects($this->once())->method('setData') - ->with(['errors' => true, 'message' => __('Incorrect CAPTCHA')])->will($this->returnValue('response')); + $this->resultJsonMock + ->expects($this->once()) + ->method('setData') + ->with(['errors' => true, 'message' => __('Incorrect CAPTCHA')]) + ->will($this->returnSelf()); $closure = function () { }; - $this->assertEquals('response', $this->model->aroundExecute($this->loginControllerMock, $closure)); + $this->assertEquals($this->resultJsonMock, $this->model->aroundExecute($this->loginControllerMock, $closure)); } /** @@ -151,7 +166,7 @@ public function testAroundExecuteCaptchaIsNotRequired($username, $requestContent { $this->requestMock->expects($this->once())->method('getContent') ->will($this->returnValue(json_encode($requestContent))); - $this->serializerMock->expects(($this->once()))->method('unserialize') + $this->serializerMock->expects($this->once())->method('unserialize') ->will($this->returnValue($requestContent)); $this->captchaMock->expects($this->once())->method('isRequired')->with($username) @@ -168,7 +183,7 @@ public function testAroundExecuteCaptchaIsNotRequired($username, $requestContent /** * @return array */ - public function aroundExecuteCaptchaIsNotRequired() + public function aroundExecuteCaptchaIsNotRequired(): array { return [ [ @@ -176,8 +191,13 @@ public function aroundExecuteCaptchaIsNotRequired() 'requestData' => ['username' => 'name', 'captcha_string' => 'string'], ], [ - 'username' => null, - 'requestData' => ['captcha_string' => 'string'], + 'username' => 'name', + 'requestData' => + [ + 'username' => 'name', + 'captcha_string' => 'string', + 'captcha_form_id' => $this->formIds[0] + ], ], ]; } From 1c1e277c19d96ed10449484998e3c0ad0c7b4c7a Mon Sep 17 00:00:00 2001 From: Ankur Raiyani Date: Fri, 1 Jun 2018 17:11:28 +0530 Subject: [PATCH 47/85] Wrong Last orders amount on dashboard #15660 Wrong order amount on dashboard on Last orders listing when having more than one website with different currencies #15660 --- .../Backend/Block/Widget/Grid/Column/Renderer/Currency.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php index ff0399e4f507f..b3f467ce37c88 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php @@ -68,10 +68,7 @@ public function __construct( $this->_storeManager = $storeManager; $this->_currencyLocator = $currencyLocator; $this->_localeCurrency = $localeCurrency; - $defaultBaseCurrencyCode = $this->_scopeConfig->getValue( - \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE, - 'default' - ); + $defaultBaseCurrencyCode = $currencyLocator->getDefaultCurrency($this->_request); $this->_defaultBaseCurrency = $currencyFactory->create()->load($defaultBaseCurrencyCode); } From 718c71ff2cc7cacc19162591dfb93b2ceb6396bd Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Fri, 1 Jun 2018 16:50:04 +0300 Subject: [PATCH 48/85] MAGETWO-86125: Sorting on price of configurable products in catalog not working properly --- .../UpdateItemsStockUponConfigChangeObserverTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php index 0e81cdf6b9123..7b82b5927d22c 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php @@ -35,7 +35,7 @@ protected function setUp() $this->event = $this->getMockBuilder(\Magento\Framework\Event::class) ->disableOriginalConstructor() - ->setMethods(['getWebsite']) + ->setMethods(['getWebsite', 'getChangedPaths']) ->getMock(); $this->eventObserver = $this->getMockBuilder(\Magento\Framework\Event\Observer::class) @@ -65,6 +65,9 @@ public function testUpdateItemsStockUponConfigChange() $this->event->expects($this->once()) ->method('getWebsite') ->will($this->returnValue($websiteId)); + $this->event->expects($this->once()) + ->method('getChangedPaths') + ->will($this->returnValue([\Magento\CatalogInventory\Model\Configuration::XML_PATH_MANAGE_STOCK])); $this->observer->execute($this->eventObserver); } From 59f6edb4495fddaa9e9d5c14402c6a2cd8ba70ae Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Fri, 1 Jun 2018 18:55:57 +0300 Subject: [PATCH 49/85] MAGETWO-86125: Sorting on price of configurable products in catalog not working properly --- app/code/Magento/Config/Model/Config.php | 49 +++++++++++++++++------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Config/Model/Config.php b/app/code/Magento/Config/Model/Config.php index 28e58db268b77..1b9773b505b93 100644 --- a/app/code/Magento/Config/Model/Config.php +++ b/app/code/Magento/Config/Model/Config.php @@ -126,10 +126,10 @@ public function save() $oldConfig = $this->_getConfig(true); - $deleteTransaction = $this->_transactionFactory->create(); /* @var $deleteTransaction \Magento\Framework\DB\Transaction */ - $saveTransaction = $this->_transactionFactory->create(); + $deleteTransaction = $this->_transactionFactory->create(); /* @var $saveTransaction \Magento\Framework\DB\Transaction */ + $saveTransaction = $this->_transactionFactory->create(); $changedPaths = []; // Extends for old config data @@ -144,9 +144,20 @@ public function save() $extraOldGroups, $oldConfig, $saveTransaction, - $deleteTransaction, - $changedPaths + $deleteTransaction ); + + if (isset($groupData['fields'])) { + $groupPath = $sectionId . '/' . $groupId; + foreach ($groupData['fields'] as $fieldId => $fieldData) { + /** @var $field \Magento\Config\Model\Config\Structure\Element\Field */ + $field = $this->_configStructure->getElement($groupPath . '/' . $fieldId); + $path = $field->getGroupPath() . '/' . $fieldId; + if ($this->isValueChanged($oldConfig, $path, $fieldData)) { + $changedPaths[] = $path; + } + } + } } try { @@ -174,6 +185,25 @@ public function save() return $this; } + /** + * Check is config value changed + * + * @param array $oldConfig + * @param string $path + * @param array $fieldData + * @return bool + */ + private function isValueChanged(array $oldConfig, string $path, array $fieldData) + { + if (isset($oldConfig[$path]['value'])) { + $result = !isset($fieldData['value']) || $oldConfig[$path]['value'] !== $fieldData['value']; + } else { + $result = empty($fieldData['inherit']); + } + + return $result; + } + /** * Process group data * @@ -185,7 +215,6 @@ public function save() * @param array &$oldConfig * @param \Magento\Framework\DB\Transaction $saveTransaction * @param \Magento\Framework\DB\Transaction $deleteTransaction - * @param array &$changedPaths * @return void * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -199,8 +228,7 @@ protected function _processGroup( array &$extraOldGroups, array &$oldConfig, \Magento\Framework\DB\Transaction $saveTransaction, - \Magento\Framework\DB\Transaction $deleteTransaction, - array &$changedPaths = [] + \Magento\Framework\DB\Transaction $deleteTransaction ) { $groupPath = $sectionPath . '/' . $groupId; $scope = $this->getScope(); @@ -300,13 +328,9 @@ protected function _processGroup( } else { $deleteTransaction->addObject($backendModel); } - if ($oldConfig[$path]['value'] !== $fieldData['value']) { - $changedPaths[] = $path; - } } elseif (!$inherit) { $backendModel->unsConfigId(); $saveTransaction->addObject($backendModel); - $changedPaths[] = $path; } } } @@ -321,8 +345,7 @@ protected function _processGroup( $extraOldGroups, $oldConfig, $saveTransaction, - $deleteTransaction, - $changedPaths + $deleteTransaction ); } } From 7b8fe3d68a946c62fd55af525b44f1ef9256ab16 Mon Sep 17 00:00:00 2001 From: Steven de Jong Date: Sat, 2 Jun 2018 16:01:30 +0200 Subject: [PATCH 50/85] #15588 Added the appEmulation to get the correct image Url for the generated sitemaps. Context had incorrect Url data. --- .../Controller/Adminhtml/Sitemap/Generate.php | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php index 67d2ce4f4f148..c46733b7d325d 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php @@ -6,8 +6,29 @@ */ namespace Magento\Sitemap\Controller\Adminhtml\Sitemap; +use Magento\Backend\App\Action; +use Magento\Store\Model\App\Emulation; + class Generate extends \Magento\Sitemap\Controller\Adminhtml\Sitemap { + /** @var \Magento\Store\Model\App\Emulation $appEmulation */ + private $appEmulation; + + /** + * Generate constructor. + * @param Action\Context $context + * @param \Magento\Store\Model\App\Emulation $appEmulation + */ + public function __construct( + Action\Context $context, + Emulation $appEmulation + ) { + $this->appEmulation = $appEmulation; + parent::__construct( + $context + ); + } + /** * Generate sitemap * @@ -23,7 +44,13 @@ public function execute() // if sitemap record exists if ($sitemap->getId()) { try { + //We need to emulate to get the correct frontend URL for the product images + $this->appEmulation->startEnvironmentEmulation($sitemap->getStoreId(), + \Magento\Framework\App\Area::AREA_FRONTEND, + true + ); $sitemap->generateXml(); + $this->appEmulation->stopEnvironmentEmulation(); $this->messageManager->addSuccess( __('The sitemap "%1" has been generated.', $sitemap->getSitemapFilename()) From 7715d655c6c316a1e058a9b25293f572b6118fc7 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun Date: Sat, 2 Jun 2018 22:25:38 +0300 Subject: [PATCH 51/85] Restore the deprecated method --- .../Checkout/Test/Block/Onepage/Shipping/Method.php | 10 ++++++++++ .../Constraint/AssertCityBasedShippingRateChanged.php | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/Method.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/Method.php index f5024d3eb7b26..767394da00bf6 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/Method.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/Method.php @@ -98,6 +98,16 @@ public function isShippingMethodAvailable(array $method) return $this->_rootElement->find($selector, Locator::SELECTOR_XPATH)->isVisible(); } + /** + * @deprecated + * @param array $method + * @return bool + */ + public function isShippingMethodAvaiable(array $method) + { + return $this->isShippingMethodAvailable($method); + } + /** * Click continue button. * diff --git a/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertCityBasedShippingRateChanged.php b/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertCityBasedShippingRateChanged.php index 482b7d213e671..ea5de8d06be00 100644 --- a/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertCityBasedShippingRateChanged.php +++ b/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertCityBasedShippingRateChanged.php @@ -30,10 +30,10 @@ public function processAssert(CheckoutOnepage $checkoutOnepage, $shippingMethod, 'Shipping rate has not been changed.' ); } - $shippingAvailability = $isShippingAvailable ? 'avaiable' : 'unavailable'; + $shippingAvailability = $isShippingAvailable ? 'available' : 'unavailable'; \PHPUnit_Framework_Assert::assertEquals( $isShippingAvailable, - $checkoutOnepage->getShippingMethodBlock()->isShippingMethodAvaiable($shippingMethod), + $checkoutOnepage->getShippingMethodBlock()->isShippingMethodAvailable($shippingMethod), "Shipping rates for {$shippingMethod['shipping_service']} should be $shippingAvailability." ); } From 498047cb31c5e34955e49aa684a7e910c4e7d941 Mon Sep 17 00:00:00 2001 From: Steven de Jong Date: Sat, 2 Jun 2018 21:51:09 +0200 Subject: [PATCH 52/85] #15588 Refactor for code neatness Travis CI. 48 | ERROR | [x] Opening parenthesis of a multi-line function call must be the last content on the line --- .../Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php index c46733b7d325d..4ef67dbabfc15 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php @@ -45,7 +45,8 @@ public function execute() if ($sitemap->getId()) { try { //We need to emulate to get the correct frontend URL for the product images - $this->appEmulation->startEnvironmentEmulation($sitemap->getStoreId(), + $this->appEmulation->startEnvironmentEmulation( + $sitemap->getStoreId(), \Magento\Framework\App\Area::AREA_FRONTEND, true ); From cf7176dd560be4f83601bb1fdd26b7fcec6b2383 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun Date: Sat, 2 Jun 2018 23:43:09 +0300 Subject: [PATCH 53/85] Add missing property, add missing throws to PHPDocs --- app/code/Magento/Ui/Model/Export/ConvertToCsv.php | 7 +++++++ app/code/Magento/Ui/Model/Export/ConvertToXml.php | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/app/code/Magento/Ui/Model/Export/ConvertToCsv.php b/app/code/Magento/Ui/Model/Export/ConvertToCsv.php index 9eba829982533..40b10749db21e 100644 --- a/app/code/Magento/Ui/Model/Export/ConvertToCsv.php +++ b/app/code/Magento/Ui/Model/Export/ConvertToCsv.php @@ -6,6 +6,7 @@ namespace Magento\Ui\Model\Export; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Exception\FileSystemException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\WriteInterface; @@ -31,11 +32,17 @@ class ConvertToCsv */ protected $pageSize = null; + /** + * @var Filter + */ + protected $filter; + /** * @param Filesystem $filesystem * @param Filter $filter * @param MetadataProvider $metadataProvider * @param int $pageSize + * @throws FileSystemException */ public function __construct( Filesystem $filesystem, diff --git a/app/code/Magento/Ui/Model/Export/ConvertToXml.php b/app/code/Magento/Ui/Model/Export/ConvertToXml.php index b707742063dbd..5f6e45a948f24 100644 --- a/app/code/Magento/Ui/Model/Export/ConvertToXml.php +++ b/app/code/Magento/Ui/Model/Export/ConvertToXml.php @@ -10,6 +10,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Convert\Excel; use Magento\Framework\Convert\ExcelFactory; +use Magento\Framework\Exception\FileSystemException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\WriteInterface; @@ -50,12 +51,18 @@ class ConvertToXml */ protected $fields; + /** + * @var Filter + */ + protected $filter; + /** * @param Filesystem $filesystem * @param Filter $filter * @param MetadataProvider $metadataProvider * @param ExcelFactory $excelFactory * @param SearchResultIteratorFactory $iteratorFactory + * @throws FileSystemException */ public function __construct( Filesystem $filesystem, @@ -88,6 +95,7 @@ protected function getOptions() * Returns DB fields list * * @return array + * @throws LocalizedException */ protected function getFields() { @@ -103,6 +111,7 @@ protected function getFields() * * @param DocumentInterface $document * @return array + * @throws LocalizedException */ public function getRowData(DocumentInterface $document) { From 63ed86492f2bdf5b2410836a71724106f9d137f0 Mon Sep 17 00:00:00 2001 From: Steven de Jong Date: Sun, 3 Jun 2018 02:50:25 +0200 Subject: [PATCH 54/85] Update for Codacy removed whitespace --- .../Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php index 4ef67dbabfc15..819608ee0235d 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php @@ -29,7 +29,7 @@ public function __construct( ); } - /** + /** * Generate sitemap * * @return void From 83ad777b941eff746883a25ad674f97609b5e9c6 Mon Sep 17 00:00:00 2001 From: Steven de Jong Date: Sun, 3 Jun 2018 19:21:08 +0200 Subject: [PATCH 55/85] #15588 Fixed incorrect image urls sitemap Change for travisCI --- .../Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php index 819608ee0235d..6872122cb1013 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php @@ -28,8 +28,8 @@ public function __construct( $context ); } - - /** + + /** * Generate sitemap * * @return void From 67778cdfc12fd10bdaf299b20ae858c28dc43d0d Mon Sep 17 00:00:00 2001 From: hitesh-wagento Date: Mon, 4 Jun 2018 11:40:13 +0530 Subject: [PATCH 56/85] [Resolved : Styling