From 929150015a1ed0c70864f77b18c2c07c030e7b92 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Wed, 26 Apr 2017 19:26:30 +0300 Subject: [PATCH 01/39] MAGETWO-65422: Write default configs to shared configuration file by app:config:dump --- app/code/Magento/Config/etc/di.xml | 4 ++++ .../App/ApplicationDumpCommandTest.php | 24 +++++++++++++++++++ .../Magento/Deploy/_files/config_data.php | 2 ++ 3 files changed, 30 insertions(+) diff --git a/app/code/Magento/Config/etc/di.xml b/app/code/Magento/Config/etc/di.xml index 1abc5e35f578f..fbfe208e11d84 100644 --- a/app/code/Magento/Config/etc/di.xml +++ b/app/code/Magento/Config/etc/di.xml @@ -183,6 +183,10 @@ + + Magento\Config\App\Config\Source\ModularConfigSource + 10 + Magento\Config\App\Config\Source\RuntimeConfigSource 100 diff --git a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ApplicationDumpCommandTest.php b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ApplicationDumpCommandTest.php index e9f650d34a6a7..6e95fb5dbf32c 100644 --- a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ApplicationDumpCommandTest.php +++ b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ApplicationDumpCommandTest.php @@ -79,6 +79,23 @@ public function setUp() // Snapshot of configuration. $this->config = $this->loadConfig(); $this->envConfig = $this->loadEnvConfig(); + + $this->writer->saveConfig( + [ + ConfigFilePool::APP_CONFIG => [ + 'system' => [ + 'default' => [ + 'web' => [ + 'test' => [ + 'test_value_3' => 'value from the file' + ] + ] + ] + ] + ] + ], + true + ); } /** @@ -197,6 +214,13 @@ private function validateSystemSection(array $config) $this->assertArrayNotHasKey('test_sensitive_environment5', $config['system']['default']['web']['test']); $this->assertArrayNotHasKey('test_sensitive_environment6', $config['system']['default']['web']['test']); $this->assertArrayNotHasKey('test_environment9', $config['system']['default']['web']['test']); + + $this->assertEquals('value from the file', $config['system']['default']['web']['test']['test_value_3']); + $this->assertEquals('GB', $config['system']['default']['general']['country']['default']); + $this->assertEquals( + 'HK,IE,MO,PA,GB', + $config['system']['default']['general']['country']['optional_zip_countries'] + ); } /** diff --git a/dev/tests/integration/testsuite/Magento/Deploy/_files/config_data.php b/dev/tests/integration/testsuite/Magento/Deploy/_files/config_data.php index d8636b4199375..de53c3935f845 100644 --- a/dev/tests/integration/testsuite/Magento/Deploy/_files/config_data.php +++ b/dev/tests/integration/testsuite/Magento/Deploy/_files/config_data.php @@ -8,7 +8,9 @@ '' => [ 'web/test/test_value_1' => 'http://local2.test/', 'web/test/test_value_2' => 5, + 'web/test/test_value_3' => 'value from the DB', 'web/test/test_sensitive' => 10, + 'general/country/default' => 'GB', 'web/test/test_sensitive1' => 'some_value1', 'web/test/test_sensitive2' => 'some_value2', From dd9d57f170282900b32513ed4c7142f81cd413e1 Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko Date: Thu, 1 Jun 2017 19:44:41 +0300 Subject: [PATCH 02/39] MAGETWO-69567: [Mainline] - Closing the image view window causes mini-cart to drop down --- lib/web/fotorama/fotorama.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/web/fotorama/fotorama.js b/lib/web/fotorama/fotorama.js index 1f489fa31d07c..62d761ecc5eda 100644 --- a/lib/web/fotorama/fotorama.js +++ b/lib/web/fotorama/fotorama.js @@ -1217,6 +1217,14 @@ fotoramaVersion = '4.6.4'; stopPropagation && e.stopPropagation && e.stopPropagation(); } + function stubEvent($el, eventType) { + $el.on(eventType, function (e) { + stopEvent(e, true); + + return false; + }); + } + function getDirectionSign(forward) { return forward ? '>' : '<'; } @@ -2150,6 +2158,11 @@ fotoramaVersion = '4.6.4'; if (o_allowFullScreen) { $fullscreenIcon.prependTo($stage); o_nativeFullScreen = FULLSCREEN && o_allowFullScreen === 'native'; + + // Due 300ms click delay on mobile divices + // we stub touchend and fallback to click. + // MAGETWO-69567 + stubEvent($fullscreenIcon, 'touchend'); } else { $fullscreenIcon.detach(); o_nativeFullScreen = false; From a232e0753026b081a83f1aa6fd47800da125a8c6 Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko Date: Fri, 2 Jun 2017 12:49:17 +0300 Subject: [PATCH 03/39] MAGETWO-69567: [Mainline] - Closing the image view window causes mini-cart to drop down - fix for typo --- lib/web/fotorama/fotorama.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/fotorama/fotorama.js b/lib/web/fotorama/fotorama.js index 62d761ecc5eda..0947164e5b0f2 100644 --- a/lib/web/fotorama/fotorama.js +++ b/lib/web/fotorama/fotorama.js @@ -2159,7 +2159,7 @@ fotoramaVersion = '4.6.4'; $fullscreenIcon.prependTo($stage); o_nativeFullScreen = FULLSCREEN && o_allowFullScreen === 'native'; - // Due 300ms click delay on mobile divices + // Due 300ms click delay on mobile devices // we stub touchend and fallback to click. // MAGETWO-69567 stubEvent($fullscreenIcon, 'touchend'); From 6b5ea625e2d1612520c3c4281b59bd49d2d3c366 Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov Date: Fri, 2 Jun 2017 13:46:47 +0300 Subject: [PATCH 04/39] MAGETWO-69607: Edition Specific BN-Codes for 2.2.x - Added the possibility to provide Braintree channel and PayPal button source via configuration --- .../Gateway/Request/ChannelDataBuilder.php | 26 ++++++--- .../Request/ChannelDataBuilderTest.php | 55 ++++++++++++++++--- app/code/Magento/Braintree/etc/di.xml | 6 ++ .../Magento/Paypal/Model/AbstractConfig.php | 3 +- .../Test/Unit/Model/AbstractConfigTest.php | 28 ++++++++-- 5 files changed, 97 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Braintree/Gateway/Request/ChannelDataBuilder.php b/app/code/Magento/Braintree/Gateway/Request/ChannelDataBuilder.php index d9a906a37954f..77e1659351bef 100644 --- a/app/code/Magento/Braintree/Gateway/Request/ChannelDataBuilder.php +++ b/app/code/Magento/Braintree/Gateway/Request/ChannelDataBuilder.php @@ -5,19 +5,16 @@ */ namespace Magento\Braintree\Gateway\Request; -use Magento\Payment\Gateway\Request\BuilderInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ProductMetadataInterface; +use Magento\Payment\Gateway\Config\Config; +use Magento\Payment\Gateway\Request\BuilderInterface; /** * Class BnCodeDataBuilder */ class ChannelDataBuilder implements BuilderInterface { - /** - * @var ProductMetadataInterface - */ - private $productMetadata; - /** * @var string */ @@ -28,14 +25,26 @@ class ChannelDataBuilder implements BuilderInterface */ private static $channelValue = 'Magento2_Cart_%s_BT'; + /** + * @var ProductMetadataInterface + */ + private $productMetadata; + + /** + * @var Config + */ + private $config; + /** * Constructor * * @param ProductMetadataInterface $productMetadata + * @param Config $config */ - public function __construct(ProductMetadataInterface $productMetadata) + public function __construct(ProductMetadataInterface $productMetadata, Config $config = null) { $this->productMetadata = $productMetadata; + $this->config = $config ?: ObjectManager::getInstance()->get(Config::class); } /** @@ -43,8 +52,9 @@ public function __construct(ProductMetadataInterface $productMetadata) */ public function build(array $buildSubject) { + $channel = $this->config->getValue('channel'); return [ - self::$channel => sprintf(self::$channelValue, $this->productMetadata->getEdition()) + self::$channel => $channel ?: sprintf(self::$channelValue, $this->productMetadata->getEdition()) ]; } } diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/ChannelDataBuilderTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/ChannelDataBuilderTest.php index b503acaf35c9e..77117b83d0a91 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/ChannelDataBuilderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/ChannelDataBuilderTest.php @@ -7,6 +7,8 @@ use Magento\Braintree\Gateway\Request\ChannelDataBuilder; use Magento\Framework\App\ProductMetadataInterface; +use Magento\Payment\Gateway\Config\Config; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * Class PaymentDataBuilderTest @@ -16,19 +18,32 @@ class ChannelDataBuilderTest extends \PHPUnit_Framework_TestCase { /** - * @var ProductMetadataInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ProductMetadataInterface|MockObject */ - private $productMetadataMock; + private $productMetadata; + + /** + * @var Config|MockObject + */ + private $config; /** * @var ChannelDataBuilder */ private $builder; + /** + * @inheritdoc + */ protected function setUp() { - $this->productMetadataMock = $this->getMock(ProductMetadataInterface::class); - $this->builder = new ChannelDataBuilder($this->productMetadataMock); + $this->productMetadata = $this->getMockBuilder(ProductMetadataInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->config = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + $this->builder = new ChannelDataBuilder($this->productMetadata, $this->config); } /** @@ -40,11 +55,37 @@ protected function setUp() public function testBuild($edition, array $expected) { $buildSubject = []; - $this->productMetadataMock->expects(static::once()) - ->method('getEdition') + + $this->config->method('getValue') + ->with(self::equalTo('channel')) + ->willReturn(null); + + $this->productMetadata->method('getEdition') ->willReturn($edition); - $this->assertEquals($expected, $this->builder->build($buildSubject)); + self::assertEquals($expected, $this->builder->build($buildSubject)); + } + + /** + * Checks a case when a channel provided via payment method configuration. + */ + public function testBuildWithChannelFromConfig() + { + $channel = 'Magento2_Cart_ConfigEdition_BT'; + + $this->config->method('getValue') + ->with(self::equalTo('channel')) + ->willReturn($channel); + + $this->productMetadata->expects(self::never()) + ->method('getEdition'); + + self::assertEquals( + [ + 'channel' => $channel + ], + $this->builder->build([]) + ); } /** diff --git a/app/code/Magento/Braintree/etc/di.xml b/app/code/Magento/Braintree/etc/di.xml index 5f8cb98e06eed..2a451e132eab0 100644 --- a/app/code/Magento/Braintree/etc/di.xml +++ b/app/code/Magento/Braintree/etc/di.xml @@ -187,6 +187,12 @@ + + + Magento\Braintree\Gateway\Config\Config + + + diff --git a/app/code/Magento/Paypal/Model/AbstractConfig.php b/app/code/Magento/Paypal/Model/AbstractConfig.php index a8551495bdd8e..0fbdfcd5137a4 100644 --- a/app/code/Magento/Paypal/Model/AbstractConfig.php +++ b/app/code/Magento/Paypal/Model/AbstractConfig.php @@ -335,7 +335,8 @@ public function isMethodSupportedForCountry($method = null, $countryCode = null) */ public function getBuildNotationCode() { - return sprintf(self::$bnCode, $this->getProductMetadata()->getEdition()); + $notationCode = $this->_scopeConfig->getValue('paypal/notation_code'); + return $notationCode ?: sprintf(self::$bnCode, $this->getProductMetadata()->getEdition()); } /** diff --git a/app/code/Magento/Paypal/Test/Unit/Model/AbstractConfigTest.php b/app/code/Magento/Paypal/Test/Unit/Model/AbstractConfigTest.php index 875169086d6f3..04e792cf5bf1d 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/AbstractConfigTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/AbstractConfigTest.php @@ -290,12 +290,16 @@ public function testIsMethodActive() $this->config->isMethodActive('method'); } + /** + * Checks a case, when notation code based on Magento edition. + */ public function testGetBuildNotationCode() { - $productMetadata = $this->getMock(ProductMetadataInterface::class, [], [], '', false); - $productMetadata->expects($this->once()) - ->method('getEdition') - ->will($this->returnValue('SomeEdition')); + $productMetadata = $this->getMockBuilder(ProductMetadataInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $productMetadata->method('getEdition') + ->willReturn('SomeEdition'); $objectManagerHelper = new ObjectManagerHelper($this); $objectManagerHelper->setBackwardCompatibleProperty( @@ -304,6 +308,20 @@ public function testGetBuildNotationCode() $productMetadata ); - $this->assertEquals('Magento_Cart_SomeEdition', $this->config->getBuildNotationCode()); + self::assertEquals('', $this->config->getBuildNotationCode()); + } + + /** + * Checks a case, when notation code should be provided from configuration. + */ + public function testBuildNotationCodeFromConfig() + { + $notationCode = 'Magento_Cart_EditionFromConfig'; + + $this->scopeConfigMock->method('getValue') + ->with(self::equalTo('paypal/notation_code')) + ->willReturn($notationCode); + + self::assertEquals($notationCode, $this->config->getBuildNotationCode()); } } From 0c3e21e80a4ce5c257d470863fdb5d0b356cbc2b Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov Date: Fri, 2 Jun 2017 14:13:16 +0300 Subject: [PATCH 05/39] MAGETWO-69607: Edition Specific BN-Codes for 2.2.x - Fixed type in the unit test --- app/code/Magento/Paypal/Test/Unit/Model/AbstractConfigTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Paypal/Test/Unit/Model/AbstractConfigTest.php b/app/code/Magento/Paypal/Test/Unit/Model/AbstractConfigTest.php index 04e792cf5bf1d..31810f720cbde 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/AbstractConfigTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/AbstractConfigTest.php @@ -308,7 +308,7 @@ public function testGetBuildNotationCode() $productMetadata ); - self::assertEquals('', $this->config->getBuildNotationCode()); + self::assertEquals('Magento_Cart_SomeEdition', $this->config->getBuildNotationCode()); } /** From af5491030d681239e090df7422ec71c38cadc813 Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov Date: Fri, 2 Jun 2017 16:51:01 +0300 Subject: [PATCH 06/39] MAGETWO-69607: Edition Specific BN-Codes for 2.2.x - Changed the config scope --- app/code/Magento/Paypal/Model/AbstractConfig.php | 2 +- app/code/Magento/Paypal/Test/Unit/Model/AbstractConfigTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Paypal/Model/AbstractConfig.php b/app/code/Magento/Paypal/Model/AbstractConfig.php index 0fbdfcd5137a4..c8142bc09562d 100644 --- a/app/code/Magento/Paypal/Model/AbstractConfig.php +++ b/app/code/Magento/Paypal/Model/AbstractConfig.php @@ -335,7 +335,7 @@ public function isMethodSupportedForCountry($method = null, $countryCode = null) */ public function getBuildNotationCode() { - $notationCode = $this->_scopeConfig->getValue('paypal/notation_code'); + $notationCode = $this->_scopeConfig->getValue('paypal/notation_code', ScopeInterface::SCOPE_STORES); return $notationCode ?: sprintf(self::$bnCode, $this->getProductMetadata()->getEdition()); } diff --git a/app/code/Magento/Paypal/Test/Unit/Model/AbstractConfigTest.php b/app/code/Magento/Paypal/Test/Unit/Model/AbstractConfigTest.php index 31810f720cbde..4940fb91da421 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/AbstractConfigTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/AbstractConfigTest.php @@ -319,7 +319,7 @@ public function testBuildNotationCodeFromConfig() $notationCode = 'Magento_Cart_EditionFromConfig'; $this->scopeConfigMock->method('getValue') - ->with(self::equalTo('paypal/notation_code')) + ->with(self::equalTo('paypal/notation_code'), self::equalTo('stores')) ->willReturn($notationCode); self::assertEquals($notationCode, $this->config->getBuildNotationCode()); From b8c9433f457842eacb553e3fde64c16e432cc521 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi Date: Tue, 6 Jun 2017 14:27:39 +0300 Subject: [PATCH 07/39] MAGETWO-68936: Billing Agreement page is not loaded if Vault enabled --- app/code/Magento/Paypal/Helper/Data.php | 27 ++++++++++--------- .../Paypal/Test/Unit/Helper/DataTest.php | 16 +++++++++-- .../Magento/Paypal/Helper/DataTest.php | 25 +++++++++++++++++ 3 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Paypal/Helper/DataTest.php diff --git a/app/code/Magento/Paypal/Helper/Data.php b/app/code/Magento/Paypal/Helper/Data.php index f354b4b3048f7..8c4af0c33677b 100644 --- a/app/code/Magento/Paypal/Helper/Data.php +++ b/app/code/Magento/Paypal/Helper/Data.php @@ -6,8 +6,11 @@ namespace Magento\Paypal\Helper; use Magento\Framework\App\ObjectManager; -use Magento\Payment\Model\Method\AbstractMethod; -use Magento\Paypal\Model\Billing\Agreement\MethodInterface; +use Magento\Payment\Api\Data\PaymentMethodInterface; +use Magento\Payment\Api\PaymentMethodListInterface; +use Magento\Payment\Model\Method\InstanceFactory; +use Magento\Payment\Model\MethodInterface; +use Magento\Paypal\Model\Billing\Agreement\MethodInterface as BillingAgreementMethodInterface; /** * Paypal Data helper @@ -45,12 +48,12 @@ class Data extends \Magento\Framework\App\Helper\AbstractHelper private $configFactory; /** - * @var \Magento\Payment\Api\PaymentMethodListInterface + * @var PaymentMethodListInterface */ private $paymentMethodList; /** - * @var \Magento\Payment\Model\Method\InstanceFactory + * @var InstanceFactory */ private $paymentMethodInstanceFactory; @@ -100,12 +103,12 @@ public function shouldAskToCreateBillingAgreement(\Magento\Paypal\Model\Config $ * * @param null|string|bool|int|\Magento\Store\Model\Store $store * @param \Magento\Quote\Model\Quote|null $quote - * @return MethodInterface[] + * @return BillingAgreementMethodInterface[] */ public function getBillingAgreementMethods($store = null, $quote = null) { $activeMethods = array_map( - function (\Magento\Payment\Api\Data\PaymentMethodInterface $method) { + function (PaymentMethodInterface $method) { return $this->getPaymentMethodInstanceFactory()->create($method); }, $this->getPaymentMethodList()->getActiveList($store) @@ -113,8 +116,8 @@ function (\Magento\Payment\Api\Data\PaymentMethodInterface $method) { $result = array_filter( $activeMethods, - function (AbstractMethod $method) use ($quote) { - return $method->isAvailable($quote) && $method instanceof MethodInterface; + function (MethodInterface $method) use ($quote) { + return $method instanceof BillingAgreementMethodInterface && $method->isAvailable($quote); } ); @@ -142,14 +145,14 @@ public function getHtmlTransactionId($methodCode, $txnId) /** * Get payment method list. * - * @return \Magento\Payment\Api\PaymentMethodListInterface + * @return PaymentMethodListInterface * @deprecated */ private function getPaymentMethodList() { if ($this->paymentMethodList === null) { $this->paymentMethodList = ObjectManager::getInstance()->get( - \Magento\Payment\Api\PaymentMethodListInterface::class + PaymentMethodListInterface::class ); } return $this->paymentMethodList; @@ -158,14 +161,14 @@ private function getPaymentMethodList() /** * Get payment method instance factory. * - * @return \Magento\Payment\Model\Method\InstanceFactory + * @return InstanceFactory * @deprecated */ private function getPaymentMethodInstanceFactory() { if ($this->paymentMethodInstanceFactory === null) { $this->paymentMethodInstanceFactory = ObjectManager::getInstance()->get( - \Magento\Payment\Model\Method\InstanceFactory::class + InstanceFactory::class ); } return $this->paymentMethodInstanceFactory; diff --git a/app/code/Magento/Paypal/Test/Unit/Helper/DataTest.php b/app/code/Magento/Paypal/Test/Unit/Helper/DataTest.php index e3ad1877e5da0..63bba7b7aef0e 100644 --- a/app/code/Magento/Paypal/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Helper/DataTest.php @@ -121,10 +121,14 @@ public function getBillingAgreementMethodsDataProvider() ->method('isAvailable') ->willReturn(true); - $methodInstanceMock = $this->getMockBuilder( + $abstractMethodInstanceMock = $this->getMockBuilder( \Magento\Payment\Model\Method\Cc::class )->disableOriginalConstructor()->getMock(); + $adapterMethodInstanceMock = $this->getMockBuilder( + \Magento\Payment\Model\Method\Adapter::class + )->disableOriginalConstructor()->getMock(); + return [ [ '1', @@ -138,7 +142,15 @@ public function getBillingAgreementMethodsDataProvider() '1', $quoteMock, [ - [$methodMock, $methodInstanceMock] + [$methodMock, $abstractMethodInstanceMock] + ], + [] + ], + [ + '1', + $quoteMock, + [ + [$methodMock, $adapterMethodInstanceMock] ], [] ] diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Helper/DataTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Helper/DataTest.php new file mode 100644 index 0000000000000..bb708f3c07a25 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/Helper/DataTest.php @@ -0,0 +1,25 @@ +create(Data::class); + + $this->assertEmpty($model->getBillingAgreementMethods()); + } +} From c6661218d10cd95783d48048f012415eed5cd980 Mon Sep 17 00:00:00 2001 From: Ihor Savchuk Date: Wed, 7 Jun 2017 12:05:14 +0300 Subject: [PATCH 08/39] MAGETWO-57975: Impossible use ExtensionInterfaceFactory - Added the ExtensionInterfaceFactory generator - Added an integration test --- app/etc/di.xml | 1 + .../Magento/Framework/Code/GeneratorTest.php | 37 ++++++++++ .../SourceClassWithNamespaceExtension.php | 14 ++++ ...espaceExtensionInterfaceFactory.php.sample | 42 +++++++++++ ...ionAttributesInterfaceFactoryGenerator.php | 70 +++++++++++++++++++ 5 files changed, 164 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespaceExtension.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceExtensionInterfaceFactory.php.sample create mode 100644 lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesInterfaceFactoryGenerator.php diff --git a/app/etc/di.xml b/app/etc/di.xml index f372bd69b9f03..7fe55c66c693c 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -674,6 +674,7 @@ + \Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceFactoryGenerator \Magento\Framework\ObjectManager\Code\Generator\Factory \Magento\Framework\ObjectManager\Code\Generator\Proxy \Magento\Framework\Interception\Code\Generator\Interceptor diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest.php b/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest.php index e15914ddedc0b..0445095041cfc 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest.php @@ -5,12 +5,17 @@ */ namespace Magento\Framework\Code; +use Magento\Framework\Code\Generator; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Interception\Code\Generator as InterceptionGenerator; use Magento\Framework\ObjectManager\Code\Generator as DIGenerator; +use Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceFactoryGenerator; +use Magento\TestFramework\Helper\Bootstrap; require_once __DIR__ . '/GeneratorTest/SourceClassWithNamespace.php'; require_once __DIR__ . '/GeneratorTest/ParentClassWithNamespace.php'; +require_once __DIR__ . '/GeneratorTest/SourceClassWithNamespaceExtension.php'; + /** * @magentoAppIsolation enabled */ @@ -51,6 +56,8 @@ protected function setUp() [ 'ioObject' => $this->_ioObject, 'generatedEntities' => [ + ExtensionAttributesInterfaceFactoryGenerator::ENTITY_TYPE => + ExtensionAttributesInterfaceFactoryGenerator::class, DIGenerator\Factory::ENTITY_TYPE => \Magento\Framework\ObjectManager\Code\Generator\Factory::class, DIGenerator\Proxy::ENTITY_TYPE => \Magento\Framework\ObjectManager\Code\Generator\Proxy::class, InterceptionGenerator\Interceptor::ENTITY_TYPE => @@ -148,4 +155,34 @@ public function testGenerateClassInterceptorWithNamespace() $this->assertEquals($expectedContent, $content); } } + + /** + * Generates a new file with ExtensionInterfaceFactory class and compares with the sample from the + * SourceClassWithNamespaceExtensionInterfaceFactory.php.sample file. + */ + public function testGenerateClassExtensionAttributesInterfaceFactoryWithNamespace() + { + $factoryClassName = self::CLASS_NAME_WITH_NAMESPACE . 'ExtensionInterfaceFactory'; + $this->varDirectory->create( + $this->varDirectory->getAbsolutePath('generation') . '/Magento/Framework/Code/GeneratorTest/' + ); + + $generatorResult = $this->_generator->generateClass($factoryClassName); + + $factory = Bootstrap::getObjectManager()->create($factoryClassName); + $object = $factory->create(); + + $this->assertEquals($generatorResult, Generator::GENERATION_SUCCESS); + $this->assertInstanceOf(self::CLASS_NAME_WITH_NAMESPACE . 'Extension', $object); + + $content = $this->_clearDocBlock( + file_get_contents( + $this->_ioObject->generateResultFileName(self::CLASS_NAME_WITH_NAMESPACE . 'ExtensionInterfaceFactory') + ) + ); + $expectedContent = $this->_clearDocBlock( + file_get_contents(__DIR__ . '/_expected/SourceClassWithNamespaceExtensionInterfaceFactory.php.sample') + ); + $this->assertEquals($expectedContent, $content); + } } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespaceExtension.php b/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespaceExtension.php new file mode 100644 index 0000000000000..15a0285f363dc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespaceExtension.php @@ -0,0 +1,14 @@ +_objectManager = $objectManager; + $this->_instanceName = $instanceName; + } + + /** + * Create class instance with specified parameters + * + * @param array $data + * @return \Magento\Framework\Code\GeneratorTest\SourceClassWithNamespaceExtension + */ + public function create(array $data = array()) + { + return $this->_objectManager->create($this->_instanceName, $data); + } +} diff --git a/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesInterfaceFactoryGenerator.php b/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesInterfaceFactoryGenerator.php new file mode 100644 index 0000000000000..12af882a46760 --- /dev/null +++ b/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesInterfaceFactoryGenerator.php @@ -0,0 +1,70 @@ +getSourceClassName(); + $resultClassName = $this->_getResultClassName(); + + if ($resultClassName !== $sourceClassName . self::$suffix) { + $this->_addError( + 'Invalid Factory class name [' . $resultClassName . ']. Use ' . $sourceClassName . self::$suffix + ); + $result = false; + } + + return $result; + } +} From fe79e4ad08b303025548dfabd5ef6590690bdfd7 Mon Sep 17 00:00:00 2001 From: Ihor Savchuk Date: Wed, 7 Jun 2017 13:03:23 +0300 Subject: [PATCH 09/39] MAGETWO-57975: Impossible use ExtensionInterfaceFactory - Added copyright to sample file --- ...ceClassWithNamespaceExtensionInterfaceFactory.php.sample | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceExtensionInterfaceFactory.php.sample b/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceExtensionInterfaceFactory.php.sample index af7a07726cacc..ea00d8d782de6 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceExtensionInterfaceFactory.php.sample +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceExtensionInterfaceFactory.php.sample @@ -1,6 +1,12 @@ Date: Thu, 8 Jun 2017 12:21:45 +0300 Subject: [PATCH 10/39] MAGETWO-69580: Unstable automated test Magento\Paypal\Test\TestCase\InContextExpressOnePageCheckoutTest failed on variation InContextExpressOnePageCheckoutTestVariation1 --- .../app/Magento/Checkout/Test/Block/Onepage/Payment/Method.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Payment/Method.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Payment/Method.php index e24f0d09183f1..09f643d2e72bb 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Payment/Method.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Payment/Method.php @@ -110,6 +110,7 @@ public function clickPayWithPaypal() */ public function inContextPaypalCheckout() { + $this->waitForElementNotVisible($this->waitElement); $this->_rootElement->find($this->placeOrderButton)->click(); $this->browser->selectWindow(); $this->waitForFormLoaded(); From d6cee8478ff90ef0e149090877cd22ac1cefcc31 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi Date: Mon, 12 Jun 2017 16:36:20 +0300 Subject: [PATCH 11/39] MAGETWO-69110: Incorrect status for order placed within Authorize.net with Fraud Filters Triggered (Filter Actions = Process as normal and report filter(s) triggered) - Added check for FDSFilterAction --- .../Magento/Authorizenet/Model/Directpost.php | 18 ++- .../Authorizenet/Model/DirectpostTest.php | 132 +++++++++++++++++- .../Magento/Authorizenet/_files/order.php | 18 ++- .../_files/transaction_details.xml | 75 ++++++++++ 4 files changed, 237 insertions(+), 6 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Authorizenet/_files/transaction_details.xml diff --git a/app/code/Magento/Authorizenet/Model/Directpost.php b/app/code/Magento/Authorizenet/Model/Directpost.php index b6a3688af5a81..f9a12a25fe781 100644 --- a/app/code/Magento/Authorizenet/Model/Directpost.php +++ b/app/code/Magento/Authorizenet/Model/Directpost.php @@ -744,7 +744,11 @@ protected function processPaymentFraudStatus(\Magento\Sales\Model\Order\Payment return $this; } - $payment->setIsFraudDetected(true); + $fdsFilterAction = (string)$fraudDetailsResponse->getFdsFilterAction(); + if ($this->fdsFilterActionIsReportOnly($fdsFilterAction) === false) { + $payment->setIsFraudDetected(true); + } + $payment->setAdditionalInformation('fraud_details', $fraudData); } catch (\Exception $e) { //this request is optional @@ -989,4 +993,16 @@ private function getPsrLogger() } return $this->psrLogger; } + + /** + * Checks if filter action is Report Only. Transactions that trigger this filter are processed as normal, + * but are also reported in the Merchant Interface as triggering this filter. + * + * @param string $fdsFilterAction + * @return bool + */ + private function fdsFilterActionIsReportOnly($fdsFilterAction) + { + return $fdsFilterAction === (string)$this->dataHelper->getFdsFilterActionLabel('report'); + } } diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php b/dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php index ace41cf69e631..4fdf45b6904ca 100644 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php +++ b/dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Authorizenet\Model; +use Magento\Framework\Simplexml\Element; use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\App\ObjectManager; @@ -12,6 +13,7 @@ use Magento\Framework\HTTP\ZendClientFactory; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Payment; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit_Framework_MockObject_MockObject as MockObject; @@ -59,7 +61,7 @@ public function testCapture() { $amount = 120.15; /** @var Payment $payment */ - $payment = $this->getPayment(); + $payment = $this->getPayment('100000002'); $transactionId = '106235225'; /** @var ZendClient|MockObject $httpClient */ @@ -99,17 +101,114 @@ public function testCapture() static::assertEquals('UK', $payment->getOrder()->getShippingAddress()->getCountryId()); } + + /** + * Verifies that order is placed in correct state according the action taken for a transaction that + * triggered one or more of the Advanced Fraud Detection Suite filters. + * + * @param string $filterAction + * @param string $orderId + * @param string $expectedOrderState + * + * @magentoConfigFixture current_store payment/authorizenet_directpost/trans_md5 TestHash + * @magentoConfigFixture current_store payment/authorizenet_directpost/login TestLogin + * @magentoDataFixture Magento/Authorizenet/_files/order.php + * @dataProvider fdsFilterActionDataProvider + */ + public function testProcessWithFdsFilterActionReportOnly($filterAction, $orderId, $expectedOrderState) + { + $responseBody = $this->getSuccessResponse($orderId); + $transactionService = $this->getTransactionService($filterAction); + $this->objectManager->addSharedInstance($transactionService, TransactionService::class); + + $this->directPost->process($responseBody); + + /** @var Payment $payment */ + $payment = $this->getPayment($orderId); + $this->objectManager->removeSharedInstance(TransactionService::class); + + static::assertEquals($expectedOrderState, $payment->getOrder()->getState()); + } + + /** + * @return array + */ + public function fdsFilterActionDataProvider() + { + return [ + ['filter_action' => 'authAndHold', 'order_id' => '100000003', 'expected_order_state' => Order::STATE_PAYMENT_REVIEW], + ['filter_action' => 'report', 'order_id' => '100000004', 'expected_order_state' => Order::STATE_PROCESSING], + ]; + } + /** - * Get order payment + * @param string $orderId + * @return array + */ + private function getSuccessResponse($orderId) + { + return [ + 'x_response_code' => '1', + 'x_response_reason_code' => '1', + 'x_response_reason_text' => 'This transaction has been approved.', + 'x_avs_code' => 'Y', + 'x_auth_code' => 'YWO2E2', + 'x_trans_id' => '40004862720', + 'x_method' => 'CC', + 'x_card_type' => 'Visa', + 'x_account_number' => 'XXXX1111', + 'x_first_name' => 'John', + 'x_last_name' => 'Smith', + 'x_company' => 'CompanyName', + 'x_address' => 'Green str, 67', + 'x_city' => 'CityM', + 'x_state' => 'Alabama', + 'x_zip' => '93930', + 'x_country' => 'US', + 'x_phone' => '3468676', + 'x_fax' => '04040404', + 'x_email' => 'user_1@example.com', + 'x_invoice_num' => $orderId, + 'x_description' => '', + 'x_type' => 'auth_only', + 'x_cust_id' => '', + 'x_ship_to_first_name' => 'John', + 'x_ship_to_last_name' => 'Smith', + 'x_ship_to_company' => 'CompanyName', + 'x_ship_to_address' => 'Green str, 67', + 'x_ship_to_city' => 'CityM', + 'x_ship_to_state' => 'Alabama', + 'x_ship_to_zip' => '93930', + 'x_ship_to_country' => 'US', + 'x_amount' => '120.15', + 'x_tax' => '0.00', + 'x_duty' => '0.00', + 'x_freight' => '5.00', + 'x_tax_exempt' => 'FALSE', + 'x_po_num' => '', + 'x_MD5_Hash' => 'C1CC5AB9D6F0481E240AD74DFF624584', + 'x_SHA2_Hash' => '', + 'x_cvv2_resp_code' => 'P', + 'x_cavv_response' => '2', + 'x_test_request' => 'false', + 'controller_action_name' => 'directpost_payment', + 'is_secure' => '1', + ]; + } + + /** + * Get order payment. + * + * @param string $orderId * @return Payment */ - private function getPayment() + private function getPayment($orderId) { /** @var FilterBuilder $filterBuilder */ $filterBuilder = $this->objectManager->get(FilterBuilder::class); $filters = [ $filterBuilder->setField(OrderInterface::INCREMENT_ID) - ->setValue('100000002') + ->setValue($orderId) ->create() ]; @@ -126,4 +225,29 @@ private function getPayment() $order = array_pop($orders); return $order->getPayment(); } + + /** + * Returns TransactionService mocked object with authorize predefined response. + * + * @param string $filterAction + * @return TransactionService|MockObject + */ + private function getTransactionService($filterAction) + { + $response = str_replace( + '{filterAction}', + $filterAction, + file_get_contents(__DIR__ . '/../_files/transaction_details.xml') + ); + + $transactionService = $this->getMockBuilder(TransactionService::class) + ->disableOriginalConstructor() + ->getMock(); + $transactionService->method('getTransactionDetails') + ->willReturn( + new Element($response) + ); + + return $transactionService; + } } diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/_files/order.php b/dev/tests/integration/testsuite/Magento/Authorizenet/_files/order.php index 1702b1313ad54..b8d632f3a87af 100644 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/_files/order.php +++ b/dev/tests/integration/testsuite/Magento/Authorizenet/_files/order.php @@ -14,7 +14,7 @@ $amount = 120.15; /** @var Payment $payment */ -$payment = $objectManager->get(Payment::class); +$payment = $objectManager->create(Payment::class); $payment ->setMethod('authorizenet_directpost') ->setAnetTransType('AUTH_ONLY') @@ -68,3 +68,19 @@ /** @var OrderRepositoryInterface $orderRepository */ $orderRepository = $objectManager->get(OrderRepositoryInterface::class); $orderRepository->save($order); + +$clonedOrder = clone $order; +$clonedOrder->setIncrementId('100000003') + ->setId(null) + ->setBillingAddress($billingAddress->setId(null)) + ->setShippingAddress($shippingAddress->setId(null)) + ->setPayment($payment->setId(null)); +$orderRepository->save($clonedOrder); + +$clonedOrder = clone $order; +$clonedOrder->setIncrementId('100000004') + ->setId(null) + ->setBillingAddress($billingAddress->setId(null)) + ->setShippingAddress($shippingAddress->setId(null)) + ->setPayment($payment->setId(null)); +$orderRepository->save($clonedOrder); diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/_files/transaction_details.xml b/dev/tests/integration/testsuite/Magento/Authorizenet/_files/transaction_details.xml new file mode 100644 index 0000000000000..0014109a62ecb --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Authorizenet/_files/transaction_details.xml @@ -0,0 +1,75 @@ + + + + Ok + + I00001 + Successful. + + + + 40004862720 + 2017-06-12T13:33:10.1Z + 2017-06-12T06:33:10.1 + authOnlyTransaction + authorizedPendingCapture + 1 + 1 + Approval + YWO2E2 + Y + P + {filterAction} + + + Amount Filter + {filterAction} + + + + 100000002 + + 120.15 + 120.15 + + 5.00 + + false + + + XXXX1111 + XXXX + Visa + + + + user_1@example.com + + + John + Smith + CompanyName +
Green str, 67
+ CityM + Alabama + 93930 + US + 3468676 + 04040404 +
+ + John + Smith + CompanyName +
Green str, 67
+ CityM + Alabama + 93930 + US +
+ false + 195.14.124.5 + Card Not Present + eCommerce +
+
From 5a78f0bfffb4691ddae73c536de39ba6dd116f14 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi Date: Tue, 13 Jun 2017 17:48:01 +0300 Subject: [PATCH 12/39] MAGETWO-69110: Incorrect status for order placed within Authorize.net with Fraud Filters Triggered (Filter Actions = Process as normal and report filter(s) triggered) - Fixed static test --- .../Authorizenet/Model/DirectpostTest.php | 29 +++++++++---------- .../_files/transaction_details.xml | 6 ++++ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php b/dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php index 4fdf45b6904ca..0b4ffd06e3796 100644 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php +++ b/dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php @@ -5,19 +5,17 @@ */ namespace Magento\Authorizenet\Model; -use Magento\Framework\Simplexml\Element; -use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\App\ObjectManager; use Magento\Framework\HTTP\ZendClient; use Magento\Framework\HTTP\ZendClientFactory; +use Magento\Framework\Simplexml\Element; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Payment; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit_Framework_MockObject_MockObject as MockObject; -use Zend_Http_Response; /** * Class contains tests for Direct Post integration @@ -74,7 +72,7 @@ public function testCapture() ->method('create') ->willReturn($httpClient); - $response = $this->getMockBuilder(Zend_Http_Response::class) + $response = $this->getMockBuilder('Zend_Http_Response') ->disableOriginalConstructor() ->setMethods(['getBody']) ->getMock(); @@ -101,7 +99,6 @@ public function testCapture() static::assertEquals('UK', $payment->getOrder()->getShippingAddress()->getCountryId()); } - /** * Verifies that order is placed in correct state according the action taken for a transaction that * triggered one or more of the Advanced Fraud Detection Suite filters. @@ -136,8 +133,16 @@ public function testProcessWithFdsFilterActionReportOnly($filterAction, $orderId public function fdsFilterActionDataProvider() { return [ - ['filter_action' => 'authAndHold', 'order_id' => '100000003', 'expected_order_state' => Order::STATE_PAYMENT_REVIEW], - ['filter_action' => 'report', 'order_id' => '100000004', 'expected_order_state' => Order::STATE_PROCESSING], + [ + 'filter_action' => 'authAndHold', + 'order_id' => '100000003', + 'expected_order_state' => Order::STATE_PAYMENT_REVIEW + ], + [ + 'filter_action' => 'report', + 'order_id' => '100000004', + 'expected_order_state' => Order::STATE_PROCESSING + ], ]; } @@ -204,17 +209,9 @@ private function getSuccessResponse($orderId) */ private function getPayment($orderId) { - /** @var FilterBuilder $filterBuilder */ - $filterBuilder = $this->objectManager->get(FilterBuilder::class); - $filters = [ - $filterBuilder->setField(OrderInterface::INCREMENT_ID) - ->setValue($orderId) - ->create() - ]; - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilters($filters) + $searchCriteria = $searchCriteriaBuilder->addFilter(OrderInterface::INCREMENT_ID, $orderId) ->create(); $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/_files/transaction_details.xml b/dev/tests/integration/testsuite/Magento/Authorizenet/_files/transaction_details.xml index 0014109a62ecb..98b9f258b0625 100644 --- a/dev/tests/integration/testsuite/Magento/Authorizenet/_files/transaction_details.xml +++ b/dev/tests/integration/testsuite/Magento/Authorizenet/_files/transaction_details.xml @@ -1,4 +1,10 @@ + Ok From f954a9150aef87d58ccc4f400815f6337234f09a Mon Sep 17 00:00:00 2001 From: Ihor Savchuk Date: Wed, 14 Jun 2017 11:18:31 +0300 Subject: [PATCH 13/39] MAGETWO-68949: [Github] "We Can't Place The Order" error #9455 - Fixed the bug - Added unit test --- app/code/Magento/Paypal/Model/Api/Nvp.php | 9 +++++++ .../Paypal/Model/Api/ProcessableException.php | 1 + .../Paypal/Test/Unit/Model/Api/NvpTest.php | 26 +++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/app/code/Magento/Paypal/Model/Api/Nvp.php b/app/code/Magento/Paypal/Model/Api/Nvp.php index 7a77279500d79..2c00fee09cdda 100644 --- a/app/code/Magento/Paypal/Model/Api/Nvp.php +++ b/app/code/Magento/Paypal/Model/Api/Nvp.php @@ -1285,6 +1285,15 @@ protected function _handleCallErrors($response) ); $this->_logger->critical($exceptionLogMessage); + /** + * The response code 10415 'Transaction has already been completed for this token' + * must not fails place order. The old Paypal interface does not lock 'Send' button + * it may result to re-send data. + */ + if (in_array((string)ProcessableException::API_TRANSACTION_HAS_BEEN_COMPLETED, $this->_callErrors)) { + return; + } + $exceptionPhrase = __('PayPal gateway has rejected request. %1', $errorMessages); /** @var \Magento\Framework\Exception\LocalizedException $exception */ diff --git a/app/code/Magento/Paypal/Model/Api/ProcessableException.php b/app/code/Magento/Paypal/Model/Api/ProcessableException.php index e455cf3128428..416bdf7c27477 100644 --- a/app/code/Magento/Paypal/Model/Api/ProcessableException.php +++ b/app/code/Magento/Paypal/Model/Api/ProcessableException.php @@ -29,6 +29,7 @@ class ProcessableException extends LocalizedException const API_MAXIMUM_AMOUNT_FILTER_DECLINE = 10538; const API_OTHER_FILTER_DECLINE = 10539; const API_ADDRESS_MATCH_FAIL = 10736; + const API_TRANSACTION_HAS_BEEN_COMPLETED = 10415; /**#@-*/ /** diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Api/NvpTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Api/NvpTest.php index dfabe34d7b4b7..c3d60186cf888 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/Api/NvpTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/Api/NvpTest.php @@ -216,4 +216,30 @@ public function testGetDebugReplacePrivateDataKeys() $debugReplacePrivateDataKeys = $this->_invokeNvpProperty($this->model, '_debugReplacePrivateDataKeys'); $this->assertEquals($debugReplacePrivateDataKeys, $this->model->getDebugReplacePrivateDataKeys()); } + + /** + * Tests case if obtained response with code 10415 'Transaction has already + * been completed for this token'. It must does not throws the exception and + * must returns response array. + */ + public function testCallTransactionHasBeenCompleted () + { + $response = "\r\n" . 'ACK[7]=Failure&L_ERRORCODE0[5]=10415' + . '&L_SHORTMESSAGE0[8]=Message.&L_LONGMESSAGE0[15]=Long%20Message.'; + $processableErrors =[10415]; + $this->curl->expects($this->once()) + ->method('read') + ->will($this->returnValue($response)); + $this->model->setProcessableErrors($processableErrors); + $this->customLoggerMock->expects($this->once()) + ->method('debug'); + $expectedResponse = [ + 'ACK' => 'Failure', + 'L_ERRORCODE0' => '10415', + 'L_SHORTMESSAGE0' => 'Message.', + 'L_LONGMESSAGE0' => 'Long Message.' + ]; + + $this->assertEquals($expectedResponse, $this->model->call('some method', ['data' => 'some data'])); + } } From df9c40648327bdab53bebf4dda84b4ed049636bd Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi Date: Thu, 15 Jun 2017 14:07:14 +0300 Subject: [PATCH 14/39] MAGETWO-69112: No request sent to Authorize.net after reordering order in admin --- .../view/adminhtml/templates/directpost/info.phtml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/info.phtml b/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/info.phtml index d7d656d13bf06..63ac183cfebdb 100644 --- a/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/info.phtml +++ b/app/code/Magento/Authorizenet/view/adminhtml/templates/directpost/info.phtml @@ -140,10 +140,6 @@ $ccExpYear = $block->getInfoData('cc_exp_year'); */ order.addExcludedPaymentMethod(''); - isAjaxRequest()): ?> - document.observe('dom:loaded', function(){ - - directPostModel = new directPost( '', 'directpost-iframe', @@ -153,9 +149,5 @@ $ccExpYear = $block->getInfoData('cc_exp_year'); 'escapeUrl($block->getUrl('*/*/save', [ '_secure' => $block->getRequest()->isSecure() ]));?>'); - - isAjaxRequest()): ?> - }); - }); From 417769445bf89475900dc2d4330137b7e9134f73 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Thu, 15 Jun 2017 17:34:06 +0300 Subject: [PATCH 15/39] MAGETWO-69584: Command config:sensitive:set does not work on the cloud --- .../Console/Command/ConfigSetCommand.php | 8 +++++--- .../Magento/Config/Model/Config/Importer.php | 7 +++---- .../Console/Command/ConfigSetCommandTest.php | 19 ++++++++++++++++++- .../Test/Unit/Model/Config/ImporterTest.php | 10 ++++++++-- .../Command/App/SensitiveConfigSetCommand.php | 2 +- .../App/SensitiveConfigSetCommandTest.php | 5 +++++ 6 files changed, 40 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Config/Console/Command/ConfigSetCommand.php b/app/code/Magento/Config/Console/Command/ConfigSetCommand.php index 4b5d821def6cf..0d7c1b044c067 100644 --- a/app/code/Magento/Config/Console/Command/ConfigSetCommand.php +++ b/app/code/Magento/Config/Console/Command/ConfigSetCommand.php @@ -138,16 +138,18 @@ protected function execute(InputInterface $input, OutputInterface $output) try { $message = $this->emulatedAreaProcessor->process(function () use ($input) { - return $this->processorFacadeFactory->create()->process( + $message = $this->processorFacadeFactory->create()->process( $input->getArgument(static::ARG_PATH), $input->getArgument(static::ARG_VALUE), $input->getOption(static::OPTION_SCOPE), $input->getOption(static::OPTION_SCOPE_CODE), $input->getOption(static::OPTION_LOCK) ); - }); - $this->hash->regenerate(System::CONFIG_TYPE); + $this->hash->regenerate(System::CONFIG_TYPE); + + return $message; + }); $output->writeln('' . $message . ''); diff --git a/app/code/Magento/Config/Model/Config/Importer.php b/app/code/Magento/Config/Model/Config/Importer.php index 58b667b9ef465..1cb3275007528 100644 --- a/app/code/Magento/Config/Model/Config/Importer.php +++ b/app/code/Magento/Config/Model/Config/Importer.php @@ -135,15 +135,14 @@ public function import(array $data) $this->scopeConfig->clean(); } - $this->state->emulateAreaCode(Area::AREA_ADMINHTML, function () use ($changedData) { + $this->state->emulateAreaCode(Area::AREA_ADMINHTML, function () use ($changedData, $data, $flag) { $this->scope->setCurrentScope(Area::AREA_ADMINHTML); // Invoke saving of new values. $this->saveProcessor->process($changedData); + $flag->setFlagData($data); + $this->flagResource->save($flag); }); - - $flag->setFlagData($data); - $this->flagResource->save($flag); } catch (\Exception $e) { throw new InvalidTransitionException(__('%1', $e->getMessage()), $e); } finally { diff --git a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSetCommandTest.php b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSetCommandTest.php index 518bf7ff69fd0..5eccffa2924d3 100644 --- a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSetCommandTest.php +++ b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSetCommandTest.php @@ -7,6 +7,7 @@ use Magento\Config\App\Config\Type\System; use Magento\Config\Console\Command\ConfigSet\ProcessorFacadeFactory; +use Magento\Config\Console\Command\ConfigSet\ProcessorFacade; use Magento\Config\Console\Command\ConfigSetCommand; use Magento\Config\Console\Command\EmulatedAdminhtmlAreaProcessor; use Magento\Deploy\Model\DeploymentConfig\ChangeDetector; @@ -48,6 +49,11 @@ class ConfigSetCommandTest extends \PHPUnit_Framework_TestCase */ private $processorFacadeFactoryMock; + /** + * @var ProcessorFacade|Mock + */ + private $processorFacadeMock; + /** * @inheritdoc */ @@ -65,6 +71,9 @@ protected function setUp() $this->processorFacadeFactoryMock = $this->getMockBuilder(ProcessorFacadeFactory::class) ->disableOriginalConstructor() ->getMock(); + $this->processorFacadeMock = $this->getMockBuilder(ProcessorFacade::class) + ->disableOriginalConstructor() + ->getMock(); $this->command = new ConfigSetCommand( $this->emulatedAreProcessorMock, @@ -79,9 +88,17 @@ public function testExecute() $this->changeDetectorMock->expects($this->once()) ->method('hasChanges') ->willReturn(false); - $this->emulatedAreProcessorMock->expects($this->once()) + $this->processorFacadeFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->processorFacadeMock); + $this->processorFacadeMock->expects($this->once()) ->method('process') ->willReturn('Some message'); + $this->emulatedAreProcessorMock->expects($this->once()) + ->method('process') + ->willReturnCallback(function ($function) { + return $function(); + }); $this->hashMock->expects($this->once()) ->method('regenerate') ->with(System::CONFIG_TYPE); diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/ImporterTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/ImporterTest.php index a8504e567142f..026fddd4cf774 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/ImporterTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/ImporterTest.php @@ -153,8 +153,14 @@ public function testImport() ->willReturn('oldScope'); $this->stateMock->expects($this->once()) ->method('emulateAreaCode') - ->with(Area::AREA_ADMINHTML, $this->anything()); - $this->scopeMock->expects($this->once()) + ->with(Area::AREA_ADMINHTML, $this->anything()) + ->willReturnCallback(function ($area, $function) { + return $function(); + }); + $this->scopeMock->expects($this->at(1)) + ->method('setCurrentScope') + ->with('adminhtml'); + $this->scopeMock->expects($this->at(2)) ->method('setCurrentScope') ->with('oldScope'); $this->flagMock->expects($this->once()) diff --git a/app/code/Magento/Deploy/Console/Command/App/SensitiveConfigSetCommand.php b/app/code/Magento/Deploy/Console/Command/App/SensitiveConfigSetCommand.php index 47d227253777b..d10f0499dfe99 100644 --- a/app/code/Magento/Deploy/Console/Command/App/SensitiveConfigSetCommand.php +++ b/app/code/Magento/Deploy/Console/Command/App/SensitiveConfigSetCommand.php @@ -155,8 +155,8 @@ protected function execute(InputInterface $input, OutputInterface $output) try { $this->emulatedAreaProcessor->process(function () use ($input, $output) { $this->facade->process($input, $output); + $this->hash->regenerate(System::CONFIG_TYPE); }); - $this->hash->regenerate(System::CONFIG_TYPE); return Cli::RETURN_SUCCESS; } catch (\Exception $e) { diff --git a/app/code/Magento/Deploy/Test/Unit/Console/Command/App/SensitiveConfigSetCommandTest.php b/app/code/Magento/Deploy/Test/Unit/Console/Command/App/SensitiveConfigSetCommandTest.php index 6254e10523f28..13cfd2f7ba0f4 100644 --- a/app/code/Magento/Deploy/Test/Unit/Console/Command/App/SensitiveConfigSetCommandTest.php +++ b/app/code/Magento/Deploy/Test/Unit/Console/Command/App/SensitiveConfigSetCommandTest.php @@ -77,6 +77,11 @@ public function testExecute() ->method('hasChanges') ->willReturn(false); $this->emulatedAreaProcessorMock->expects($this->once()) + ->method('process') + ->willReturnCallback(function ($function) { + return $function(); + }); + $this->facadeMock->expects($this->once()) ->method('process'); $this->hashMock->expects($this->once()) ->method('regenerate') From 6dbbb7421aa50c89288ad82a1c8d68ca5bb013b3 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Thu, 15 Jun 2017 18:06:43 +0300 Subject: [PATCH 16/39] MAGETWO-69584: Command config:sensitive:set does not work on the cloud --- .../Magento/Config/Test/Unit/Model/Config/ImporterTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/ImporterTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/ImporterTest.php index 026fddd4cf774..a0712cef4604a 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/ImporterTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/ImporterTest.php @@ -155,11 +155,12 @@ public function testImport() ->method('emulateAreaCode') ->with(Area::AREA_ADMINHTML, $this->anything()) ->willReturnCallback(function ($area, $function) { + $this->assertEquals(Area::AREA_ADMINHTML, $area); return $function(); }); $this->scopeMock->expects($this->at(1)) ->method('setCurrentScope') - ->with('adminhtml'); + ->with(Area::AREA_ADMINHTML); $this->scopeMock->expects($this->at(2)) ->method('setCurrentScope') ->with('oldScope'); From d7b1b8ae63139720654ad92c707c060d6460fa2e Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Fri, 16 Jun 2017 10:22:32 +0300 Subject: [PATCH 17/39] MAGETWO-69584: Command config:sensitive:set does not work on the cloud --- .../Command/ConfigSet/ProcessorFacade.php | 16 +++++++++++++++- .../Config/Console/Command/ConfigSetCommand.php | 13 ------------- .../Command/ConfigSet/ProcessorFacadeTest.php | 17 ++++++++++++++++- .../Console/Command/ConfigSetCommandTest.php | 14 -------------- 4 files changed, 31 insertions(+), 29 deletions(-) diff --git a/app/code/Magento/Config/Console/Command/ConfigSet/ProcessorFacade.php b/app/code/Magento/Config/Console/Command/ConfigSet/ProcessorFacade.php index 62e7d05b2d4c2..e4a99f714ca7c 100644 --- a/app/code/Magento/Config/Console/Command/ConfigSet/ProcessorFacade.php +++ b/app/code/Magento/Config/Console/Command/ConfigSet/ProcessorFacade.php @@ -12,6 +12,8 @@ use Magento\Framework\Exception\ConfigurationMismatchException; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\ValidatorException; +use Magento\Deploy\Model\DeploymentConfig\Hash; +use Magento\Config\App\Config\Type\System; /** * Processor facade for config:set command. @@ -46,19 +48,29 @@ class ProcessorFacade */ private $configSetProcessorFactory; + /** + * The hash manager. + * + * @var Hash + */ + private $hash; + /** * @param ValidatorInterface $scopeValidator The scope validator * @param PathValidator $pathValidator The path validator * @param ConfigSetProcessorFactory $configSetProcessorFactory The factory for config:set processors + * @param Hash $hash The hash manager */ public function __construct( ValidatorInterface $scopeValidator, PathValidator $pathValidator, - ConfigSetProcessorFactory $configSetProcessorFactory + ConfigSetProcessorFactory $configSetProcessorFactory, + Hash $hash ) { $this->scopeValidator = $scopeValidator; $this->pathValidator = $pathValidator; $this->configSetProcessorFactory = $configSetProcessorFactory; + $this->hash = $hash; } /** @@ -93,6 +105,8 @@ public function process($path, $value, $scope, $scopeCode, $lock) // The processing flow depends on --lock option. $processor->process($path, $value, $scope, $scopeCode); + $this->hash->regenerate(System::CONFIG_TYPE); + return $message; } } diff --git a/app/code/Magento/Config/Console/Command/ConfigSetCommand.php b/app/code/Magento/Config/Console/Command/ConfigSetCommand.php index 0d7c1b044c067..8e26d1f12f2de 100644 --- a/app/code/Magento/Config/Console/Command/ConfigSetCommand.php +++ b/app/code/Magento/Config/Console/Command/ConfigSetCommand.php @@ -8,7 +8,6 @@ use Magento\Config\App\Config\Type\System; use Magento\Config\Console\Command\ConfigSet\ProcessorFacadeFactory; use Magento\Deploy\Model\DeploymentConfig\ChangeDetector; -use Magento\Deploy\Model\DeploymentConfig\Hash; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Console\Cli; use Symfony\Component\Console\Command\Command; @@ -46,13 +45,6 @@ class ConfigSetCommand extends Command */ private $changeDetector; - /** - * The hash manager. - * - * @var Hash - */ - private $hash; - /** * The factory for processor facade. * @@ -63,18 +55,15 @@ class ConfigSetCommand extends Command /** * @param EmulatedAdminhtmlAreaProcessor $emulatedAreaProcessor Emulator adminhtml area for CLI command * @param ChangeDetector $changeDetector The config change detector - * @param Hash $hash The hash manager * @param ProcessorFacadeFactory $processorFacadeFactory The factory for processor facade */ public function __construct( EmulatedAdminhtmlAreaProcessor $emulatedAreaProcessor, ChangeDetector $changeDetector, - Hash $hash, ProcessorFacadeFactory $processorFacadeFactory ) { $this->emulatedAreaProcessor = $emulatedAreaProcessor; $this->changeDetector = $changeDetector; - $this->hash = $hash; $this->processorFacadeFactory = $processorFacadeFactory; parent::__construct(); @@ -146,8 +135,6 @@ protected function execute(InputInterface $input, OutputInterface $output) $input->getOption(static::OPTION_LOCK) ); - $this->hash->regenerate(System::CONFIG_TYPE); - return $message; }); diff --git a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ProcessorFacadeTest.php b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ProcessorFacadeTest.php index 5a966b69911d3..517a389ba067e 100644 --- a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ProcessorFacadeTest.php +++ b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ProcessorFacadeTest.php @@ -15,6 +15,8 @@ use Magento\Framework\Exception\ValidatorException; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\ConfigurationMismatchException; +use Magento\Deploy\Model\DeploymentConfig\Hash; +use Magento\Config\App\Config\Type\System; use PHPUnit_Framework_MockObject_MockObject as Mock; /** @@ -49,6 +51,11 @@ class ProcessorFacadeTest extends \PHPUnit_Framework_TestCase */ private $processorMock; + /** + * @var Hash|Mock + */ + private $hashMock; + /** * @inheritdoc */ @@ -69,10 +76,15 @@ protected function setUp() ->method('create') ->willReturn($this->processorMock); + $this->hashMock = $this->getMockBuilder(Hash::class) + ->disableOriginalConstructor() + ->getMock(); + $this->model = new ProcessorFacade( $this->scopeValidatorMock, $this->pathValidatorMock, - $this->configSetProcessorFactoryMock + $this->configSetProcessorFactoryMock, + $this->hashMock ); } @@ -91,6 +103,9 @@ public function testProcess() $this->processorMock->expects($this->once()) ->method('process') ->with('test/test/test', 'test', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null); + $this->hashMock->expects($this->once()) + ->method('regenerate') + ->with(System::CONFIG_TYPE); $this->assertSame( 'Value was saved.', diff --git a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSetCommandTest.php b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSetCommandTest.php index 5eccffa2924d3..aeb2cc3be7bdd 100644 --- a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSetCommandTest.php +++ b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSetCommandTest.php @@ -5,13 +5,11 @@ */ namespace Magento\Config\Test\Unit\Console\Command; -use Magento\Config\App\Config\Type\System; use Magento\Config\Console\Command\ConfigSet\ProcessorFacadeFactory; use Magento\Config\Console\Command\ConfigSet\ProcessorFacade; use Magento\Config\Console\Command\ConfigSetCommand; use Magento\Config\Console\Command\EmulatedAdminhtmlAreaProcessor; use Magento\Deploy\Model\DeploymentConfig\ChangeDetector; -use Magento\Deploy\Model\DeploymentConfig\Hash; use Magento\Framework\Console\Cli; use Magento\Framework\Exception\ValidatorException; use PHPUnit_Framework_MockObject_MockObject as Mock; @@ -39,11 +37,6 @@ class ConfigSetCommandTest extends \PHPUnit_Framework_TestCase */ private $changeDetectorMock; - /** - * @var Hash|Mock - */ - private $hashMock; - /** * @var ProcessorFacadeFactory|Mock */ @@ -65,9 +58,6 @@ protected function setUp() $this->changeDetectorMock = $this->getMockBuilder(ChangeDetector::class) ->disableOriginalConstructor() ->getMock(); - $this->hashMock = $this->getMockBuilder(Hash::class) - ->disableOriginalConstructor() - ->getMock(); $this->processorFacadeFactoryMock = $this->getMockBuilder(ProcessorFacadeFactory::class) ->disableOriginalConstructor() ->getMock(); @@ -78,7 +68,6 @@ protected function setUp() $this->command = new ConfigSetCommand( $this->emulatedAreProcessorMock, $this->changeDetectorMock, - $this->hashMock, $this->processorFacadeFactoryMock ); } @@ -99,9 +88,6 @@ public function testExecute() ->willReturnCallback(function ($function) { return $function(); }); - $this->hashMock->expects($this->once()) - ->method('regenerate') - ->with(System::CONFIG_TYPE); $tester = new CommandTester($this->command); $tester->execute([ From dc1f009e85c2b796c6f74b5248544c2a141216ca Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Fri, 16 Jun 2017 11:07:39 +0300 Subject: [PATCH 18/39] MAGETWO-69584: Command config:sensitive:set does not work on the cloud --- .../Console/Command/ConfigSet/ConfigSetProcessorInterface.php | 2 +- .../Config/Console/Command/ConfigSet/ProcessorFacade.php | 2 +- app/code/Magento/Config/Console/Command/ConfigSetCommand.php | 2 +- app/code/Magento/Config/Model/PreparedValueFactory.php | 2 +- .../Deploy/Console/Command/App/SensitiveConfigSetCommand.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Config/Console/Command/ConfigSet/ConfigSetProcessorInterface.php b/app/code/Magento/Config/Console/Command/ConfigSet/ConfigSetProcessorInterface.php index ff9bb249f927a..5adbbdbbeded5 100644 --- a/app/code/Magento/Config/Console/Command/ConfigSet/ConfigSetProcessorInterface.php +++ b/app/code/Magento/Config/Console/Command/ConfigSet/ConfigSetProcessorInterface.php @@ -18,7 +18,7 @@ interface ConfigSetProcessorInterface /** * Processes config:set command. * - * @param string $path The configuration path in format group/section/field_name + * @param string $path The configuration path in format section/group/field_name * @param string $value The configuration value * @param string $scope The configuration scope (default, website, or store) * @param string $scopeCode The scope code diff --git a/app/code/Magento/Config/Console/Command/ConfigSet/ProcessorFacade.php b/app/code/Magento/Config/Console/Command/ConfigSet/ProcessorFacade.php index e4a99f714ca7c..f42c8c09fbc26 100644 --- a/app/code/Magento/Config/Console/Command/ConfigSet/ProcessorFacade.php +++ b/app/code/Magento/Config/Console/Command/ConfigSet/ProcessorFacade.php @@ -76,7 +76,7 @@ public function __construct( /** * Processes config:set command. * - * @param string $path The configuration path in format group/section/field_name + * @param string $path The configuration path in format section/group/field_name * @param string $value The configuration value * @param string $scope The configuration scope (default, website, or store) * @param string $scopeCode The scope code diff --git a/app/code/Magento/Config/Console/Command/ConfigSetCommand.php b/app/code/Magento/Config/Console/Command/ConfigSetCommand.php index 8e26d1f12f2de..d28991f5ef48c 100644 --- a/app/code/Magento/Config/Console/Command/ConfigSetCommand.php +++ b/app/code/Magento/Config/Console/Command/ConfigSetCommand.php @@ -80,7 +80,7 @@ protected function configure() new InputArgument( static::ARG_PATH, InputArgument::REQUIRED, - 'Configuration path in format group/section/field_name' + 'Configuration path in format section/group/field_name' ), new InputArgument(static::ARG_VALUE, InputArgument::REQUIRED, 'Configuration value'), new InputOption( diff --git a/app/code/Magento/Config/Model/PreparedValueFactory.php b/app/code/Magento/Config/Model/PreparedValueFactory.php index 842b1dff002bc..a47c8ee24c102 100644 --- a/app/code/Magento/Config/Model/PreparedValueFactory.php +++ b/app/code/Magento/Config/Model/PreparedValueFactory.php @@ -73,7 +73,7 @@ public function __construct( /** * Returns instance of Value with defined properties. * - * @param string $path The configuration path in format group/section/field_name + * @param string $path The configuration path in format section/group/field_name * @param string $value The configuration value * @param string $scope The configuration scope (default, website, or store) * @param string|int|null $scopeCode The scope code diff --git a/app/code/Magento/Deploy/Console/Command/App/SensitiveConfigSetCommand.php b/app/code/Magento/Deploy/Console/Command/App/SensitiveConfigSetCommand.php index d10f0499dfe99..95daa272f1470 100644 --- a/app/code/Magento/Deploy/Console/Command/App/SensitiveConfigSetCommand.php +++ b/app/code/Magento/Deploy/Console/Command/App/SensitiveConfigSetCommand.php @@ -104,7 +104,7 @@ protected function configure() $this->addArgument( self::INPUT_ARGUMENT_PATH, InputArgument::OPTIONAL, - 'Configuration path for example group/section/field_name' + 'Configuration path for example section/group/field_name' ); $this->addArgument( self::INPUT_ARGUMENT_VALUE, From 8dc164e4646d4de6ff8994ca4d059fab11d25947 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Fri, 16 Jun 2017 11:12:23 +0300 Subject: [PATCH 19/39] MAGETWO-69584: Command config:sensitive:set does not work on the cloud --- app/code/Magento/Config/Console/Command/ConfigSetCommand.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/code/Magento/Config/Console/Command/ConfigSetCommand.php b/app/code/Magento/Config/Console/Command/ConfigSetCommand.php index d28991f5ef48c..28d99d2a30559 100644 --- a/app/code/Magento/Config/Console/Command/ConfigSetCommand.php +++ b/app/code/Magento/Config/Console/Command/ConfigSetCommand.php @@ -127,15 +127,13 @@ protected function execute(InputInterface $input, OutputInterface $output) try { $message = $this->emulatedAreaProcessor->process(function () use ($input) { - $message = $this->processorFacadeFactory->create()->process( + return $this->processorFacadeFactory->create()->process( $input->getArgument(static::ARG_PATH), $input->getArgument(static::ARG_VALUE), $input->getOption(static::OPTION_SCOPE), $input->getOption(static::OPTION_SCOPE_CODE), $input->getOption(static::OPTION_LOCK) ); - - return $message; }); $output->writeln('' . $message . ''); From 718533ff1c0fd42f3fa8edce16424fbeb2274545 Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin Date: Fri, 16 Jun 2017 17:25:42 +0300 Subject: [PATCH 20/39] MAGETWO-57846: [Github] New Order Status config in payment method is useless #5860 --- .../Order/Payment/State/AuthorizeCommand.php | 38 ++- .../Order/Payment/State/CaptureCommand.php | 58 ++-- .../Order/Payment/State/OrderCommand.php | 56 ++-- .../RegisterCaptureNotificationCommand.php | 52 ++-- .../Sales/Model/Order/StatusResolver.php | 26 ++ .../Payment/State/AuthorizeCommandTest.php | 260 +++++++----------- .../Payment/State/CaptureCommandTest.php | 260 ++++++++---------- .../Order/Payment/State/OrderCommandTest.php | 165 +++++++++++ ...RegisterCaptureNotificationCommandTest.php | 165 +++++++++++ .../Unit/Model/Order/StatusResolverTest.php | 108 ++++++++ 10 files changed, 813 insertions(+), 375 deletions(-) create mode 100644 app/code/Magento/Sales/Model/Order/StatusResolver.php create mode 100644 app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/OrderCommandTest.php create mode 100644 app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/RegisterCaptureNotificationCommandTest.php create mode 100644 app/code/Magento/Sales/Test/Unit/Model/Order/StatusResolverTest.php diff --git a/app/code/Magento/Sales/Model/Order/Payment/State/AuthorizeCommand.php b/app/code/Magento/Sales/Model/Order/Payment/State/AuthorizeCommand.php index 8620f0346eccc..9b60f9b2caa12 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/State/AuthorizeCommand.php +++ b/app/code/Magento/Sales/Model/Order/Payment/State/AuthorizeCommand.php @@ -5,16 +5,28 @@ */ namespace Magento\Sales\Model\Order\Payment\State; +use Magento\Framework\App\ObjectManager; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\Data\OrderPaymentInterface; use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Payment; +use Magento\Sales\Model\Order\StatusResolver; -/** - * Class AuthorizeCommand - */ class AuthorizeCommand implements CommandInterface { + /** + * @var StatusResolver + */ + private $statusResolver; + + /** + * @param StatusResolver|null $statusResolver + */ + public function __construct(StatusResolver $statusResolver = null) + { + $this->statusResolver = $statusResolver + ? : ObjectManager::getInstance()->get(StatusResolver::class); + } + /** * @param OrderPaymentInterface $payment * @param string|float $amount @@ -24,14 +36,12 @@ class AuthorizeCommand implements CommandInterface public function execute(OrderPaymentInterface $payment, $amount, OrderInterface $order) { $state = Order::STATE_PROCESSING; - $status = false; - $formattedAmount = $order->getBaseCurrency()->formatTxt($amount); + $status = null; + $message = 'Authorized amount of %1.'; if ($payment->getIsTransactionPending()) { $state = Order::STATE_PAYMENT_REVIEW; $message = 'We will authorize %1 after the payment is approved at the payment gateway.'; - } else { - $message = 'Authorized amount of %1.'; } if ($payment->getIsFraudDetected()) { @@ -39,12 +49,20 @@ public function execute(OrderPaymentInterface $payment, $amount, OrderInterface $status = Order::STATUS_FRAUD; $message .= ' Order is suspended as its authorizing amount %1 is suspected to be fraudulent.'; } - $this->setOrderStateAndStatus($order, $status, $state); - return __($message, $formattedAmount); + if (!isset($status)) { + $status = $this->statusResolver->getOrderStatusByState($order, $state); + } + + $order->setState($state); + $order->setStatus($status); + + return __($message, $order->getBaseCurrency()->formatTxt($amount)); } /** + * @deprecated Replaced by a StatusResolver class call. + * * @param Order $order * @param string $status * @param string $state diff --git a/app/code/Magento/Sales/Model/Order/Payment/State/CaptureCommand.php b/app/code/Magento/Sales/Model/Order/Payment/State/CaptureCommand.php index 4baac69614b49..4457a7ce24a06 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/State/CaptureCommand.php +++ b/app/code/Magento/Sales/Model/Order/Payment/State/CaptureCommand.php @@ -5,54 +5,70 @@ */ namespace Magento\Sales\Model\Order\Payment\State; +use Magento\Framework\App\ObjectManager; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\Data\OrderPaymentInterface; -use Magento\Sales\Model\Order as SalesOrder; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\StatusResolver; -/** - * Class CaptureCommand - */ class CaptureCommand implements CommandInterface { /** - * Run command - * + * @var StatusResolver + */ + private $statusResolver; + + /** + * @param StatusResolver|null $statusResolver + */ + public function __construct(StatusResolver $statusResolver = null) + { + $this->statusResolver = $statusResolver + ? : ObjectManager::getInstance()->get(StatusResolver::class); + } + + /** * @param OrderPaymentInterface $payment - * @param string|float|int $amount + * @param string|float $amount * @param OrderInterface $order - * @return string + * @return \Magento\Framework\Phrase */ public function execute(OrderPaymentInterface $payment, $amount, OrderInterface $order) { - $state = SalesOrder::STATE_PROCESSING; - $status = false; - $formattedAmount = $order->getBaseCurrency()->formatTxt($amount); + $state = Order::STATE_PROCESSING; + $status = null; + $message = 'Captured amount of %1 online.'; if ($payment->getIsTransactionPending()) { - $state = SalesOrder::STATE_PAYMENT_REVIEW; + $state = Order::STATE_PAYMENT_REVIEW; $message = 'An amount of %1 will be captured after being approved at the payment gateway.'; - } else { - // normal online capture: invoice is marked as "paid" - $message = 'Captured amount of %1 online.'; } if ($payment->getIsFraudDetected()) { - $state = SalesOrder::STATE_PAYMENT_REVIEW; - $status = SalesOrder::STATUS_FRAUD; + $state = Order::STATE_PAYMENT_REVIEW; + $status = Order::STATUS_FRAUD; $message .= ' Order is suspended as its capturing amount %1 is suspected to be fraudulent.'; } - $this->setOrderStateAndStatus($order, $status, $state); - return __($message, $formattedAmount); + if (!isset($status)) { + $status = $this->statusResolver->getOrderStatusByState($order, $state); + } + + $order->setState($state); + $order->setStatus($status); + + return __($message, $order->getBaseCurrency()->formatTxt($amount)); } /** - * @param SalesOrder $order + * @deprecated Replaced by a StatusResolver class call. + * + * @param Order $order * @param string $status * @param string $state * @return void */ - protected function setOrderStateAndStatus(SalesOrder $order, $status, $state) + protected function setOrderStateAndStatus(Order $order, $status, $state) { if (!$status) { $status = $order->getConfig()->getStateDefaultStatus($state); diff --git a/app/code/Magento/Sales/Model/Order/Payment/State/OrderCommand.php b/app/code/Magento/Sales/Model/Order/Payment/State/OrderCommand.php index 464bfdd6b12a5..5e3ffce6fd965 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/State/OrderCommand.php +++ b/app/code/Magento/Sales/Model/Order/Payment/State/OrderCommand.php @@ -5,46 +5,64 @@ */ namespace Magento\Sales\Model\Order\Payment\State; +use Magento\Framework\App\ObjectManager; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\Data\OrderPaymentInterface; use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\StatusResolver; -/** - * Class Order - */ class OrderCommand implements CommandInterface { /** - * Run command - * + * @var StatusResolver + */ + private $statusResolver; + + /** + * @param StatusResolver|null $statusResolver + */ + public function __construct(StatusResolver $statusResolver = null) + { + $this->statusResolver = $statusResolver + ? : ObjectManager::getInstance()->get(StatusResolver::class); + } + + /** * @param OrderPaymentInterface $payment - * @param string|float|int $amount + * @param string|float $amount * @param OrderInterface $order * @return string */ public function execute(OrderPaymentInterface $payment, $amount, OrderInterface $order) { $state = Order::STATE_PROCESSING; - $status = false; - $formattedAmount = $order->getBaseCurrency()->formatTxt($amount); + $status = null; + $message = 'Ordered amount of %1'; + if ($payment->getIsTransactionPending()) { - $message = __( - 'The order amount of %1 is pending approval on the payment gateway.', - $formattedAmount - ); $state = Order::STATE_PAYMENT_REVIEW; - if ($payment->getIsFraudDetected()) { - $status = Order::STATUS_FRAUD; - } - } else { - $message = __('Ordered amount of %1', $formattedAmount); + $message = 'The order amount of %1 is pending approval on the payment gateway.'; + } + + if ($payment->getIsFraudDetected()) { + $state = Order::STATE_PAYMENT_REVIEW; + $status = Order::STATUS_FRAUD; + $message = 'The order amount of %1 is pending approval on the payment gateway.'; } - $this->setOrderStateAndStatus($order, $status, $state); - return $message; + if (!isset($status)) { + $status = $this->statusResolver->getOrderStatusByState($order, $state); + } + + $order->setState($state); + $order->setStatus($status); + + return __($message, $order->getBaseCurrency()->formatTxt($amount)); } /** + * @deprecated Replaced by a StatusResolver class call. + * * @param Order $order * @param string $status * @param string $state diff --git a/app/code/Magento/Sales/Model/Order/Payment/State/RegisterCaptureNotificationCommand.php b/app/code/Magento/Sales/Model/Order/Payment/State/RegisterCaptureNotificationCommand.php index b333733c6aa0c..605295f1fbc0c 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/State/RegisterCaptureNotificationCommand.php +++ b/app/code/Magento/Sales/Model/Order/Payment/State/RegisterCaptureNotificationCommand.php @@ -5,16 +5,29 @@ */ namespace Magento\Sales\Model\Order\Payment\State; +use Magento\Framework\App\ObjectManager; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\Data\OrderPaymentInterface; use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Payment; +use Magento\Sales\Model\Order\StatusResolver; class RegisterCaptureNotificationCommand implements CommandInterface { /** - * Run command - * + * @var StatusResolver + */ + private $statusResolver; + + /** + * @param StatusResolver|null $statusResolver + */ + public function __construct(StatusResolver $statusResolver = null) + { + $this->statusResolver = $statusResolver + ? : ObjectManager::getInstance()->get(StatusResolver::class); + } + + /** * @param OrderPaymentInterface $payment * @param string|float|int $amount * @param OrderInterface $order @@ -22,35 +35,34 @@ class RegisterCaptureNotificationCommand implements CommandInterface */ public function execute(OrderPaymentInterface $payment, $amount, OrderInterface $order) { - /** - * @var $payment Payment - */ $state = Order::STATE_PROCESSING; - $status = false; - $formattedAmount = $order->getBaseCurrency()->formatTxt($amount); + $status = null; + $message = 'Registered notification about captured amount of %1.'; + if ($payment->getIsTransactionPending()) { - $message = __( - 'An amount of %1 will be captured after being approved at the payment gateway.', - $formattedAmount - ); $state = Order::STATE_PAYMENT_REVIEW; - } else { - $message = __('Registered notification about captured amount of %1.', $formattedAmount); + $message = 'An amount of %1 will be captured after being approved at the payment gateway.'; } + if ($payment->getIsFraudDetected()) { $state = Order::STATE_PAYMENT_REVIEW; - $message = __( - 'Order is suspended as its capture amount %1 is suspected to be fraudulent.', - $formattedAmount - ); $status = Order::STATUS_FRAUD; + $message = 'Order is suspended as its capture amount %1 is suspected to be fraudulent.'; } - $this->setOrderStateAndStatus($order, $status, $state); - return $message; + if (!isset($status)) { + $status = $this->statusResolver->getOrderStatusByState($order, $state); + } + + $order->setState($state); + $order->setStatus($status); + + return __($message, $order->getBaseCurrency()->formatTxt($amount)); } /** + * @deprecated Replaced by a StatusResolver class call. + * * @param Order $order * @param string $status * @param string $state diff --git a/app/code/Magento/Sales/Model/Order/StatusResolver.php b/app/code/Magento/Sales/Model/Order/StatusResolver.php new file mode 100644 index 0000000000000..b83319de86a55 --- /dev/null +++ b/app/code/Magento/Sales/Model/Order/StatusResolver.php @@ -0,0 +1,26 @@ +getPayment()->getMethodInstance() + ->getConfigData('order_status'); + + return array_key_exists($paymentMethodOrderStatus, $order->getConfig()->getStateStatuses($state)) + ? $paymentMethodOrderStatus + : $order->getConfig()->getStateDefaultStatus($state); + } +} diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/AuthorizeCommandTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/AuthorizeCommandTest.php index 9705665af80e4..914d70cc4bf81 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/AuthorizeCommandTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/AuthorizeCommandTest.php @@ -6,208 +6,162 @@ namespace Magento\Sales\Test\Unit\Model\Order\Payment\State; use Magento\Directory\Model\Currency; +use Magento\Sales\Api\Data\OrderPaymentInterface; use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Config; -use Magento\Sales\Model\Order\Payment; use Magento\Sales\Model\Order\Payment\State\AuthorizeCommand; +use Magento\Sales\Model\Order\StatusResolver; use PHPUnit_Framework_MockObject_MockObject as MockObject; /** - * Class AuthorizeCommandTest - * @see \Magento\Sales\Model\Order\Payment\State\AuthorizeCommand + * @see AuthorizeCommand */ class AuthorizeCommandTest extends \PHPUnit_Framework_TestCase { /** - * @var Payment|MockObject + * @var float */ - private $payment; + private $amount = 10.00; /** - * @var Order|MockObject + * @var string */ - private $order; + private $newOrderStatus = 'custom_status'; /** - * @var Currency|MockObject + * @see AuthorizeCommand::execute + * + * @param bool $isTransactionPending + * @param bool $isFraudDetected + * @param string $expectedState + * @param string $expectedStatus + * @param string $expectedMessage + * + * @dataProvider commandResultDataProvider */ - private $currency; + public function testExecute( + $isTransactionPending, + $isFraudDetected, + $expectedState, + $expectedStatus, + $expectedMessage + ) { + $actualReturn = (new AuthorizeCommand($this->getStatusResolver()))->execute( + $this->getPayment($isTransactionPending, $isFraudDetected), + $this->amount, + $this->getOrder() + ); - /** - * @var Config|MockObject - */ - private $config; + $this->assertOrderStateAndStatus($this->getOrder(), $expectedState, $expectedStatus); + self::assertEquals(__($expectedMessage, $this->amount), $actualReturn); + } /** - * @var AuthorizeCommand + * @return array */ - private $command; + public function commandResultDataProvider() + { + return [ + [ + false, + false, + Order::STATE_PROCESSING, + $this->newOrderStatus, + 'Authorized amount of %1.' + ], + [ + true, + false, + Order::STATE_PAYMENT_REVIEW, + $this->newOrderStatus, + 'We will authorize %1 after the payment is approved at the payment gateway.' + ], + [ + false, + true, + Order::STATE_PAYMENT_REVIEW, + Order::STATUS_FRAUD, + 'Authorized amount of %1.' . + ' Order is suspended as its authorizing amount %1 is suspected to be fraudulent.' + ], + [ + true, + true, + Order::STATE_PAYMENT_REVIEW, + Order::STATUS_FRAUD, + 'We will authorize %1 after the payment is approved at the payment gateway.' . + ' Order is suspended as its authorizing amount %1 is suspected to be fraudulent.' + ], + ]; + } /** - * @var int + * @return StatusResolver|MockObject */ - private $amount = 45; - - protected function setUp() + private function getStatusResolver() { - $this->currency = $this->getMockBuilder(Currency::class) - ->disableOriginalConstructor() - ->setMethods(['formatTxt']) - ->getMock(); - - $this->config = $this->getMockBuilder(Config::class) + $statusResolver = $this->getMockBuilder(StatusResolver::class) ->disableOriginalConstructor() - ->setMethods(['getStateDefaultStatus']) ->getMock(); + $statusResolver->method('getOrderStatusByState') + ->willReturn($this->newOrderStatus); - $this->payment = $this->getMockBuilder(Payment::class) - ->disableOriginalConstructor() - ->setMethods(['getIsTransactionPending', 'getIsFraudDetected']) - ->getMock(); - $this->order = $this->getMockBuilder(Order::class) - ->disableOriginalConstructor() - ->setMethods(['getBaseCurrency', 'getConfig', 'setState', 'setStatus']) - ->getMock(); - - $this->order->expects(static::once()) - ->method('getBaseCurrency') - ->willReturn($this->currency); - $this->currency->expects(static::once()) - ->method('formatTxt') - ->with($this->amount) - ->willReturn($this->amount); - - $this->command = new AuthorizeCommand(); + return $statusResolver; } /** - * @covers \Magento\Sales\Model\Order\Payment\State\AuthorizeCommand::execute + * @return Order|MockObject */ - public function testExecute() + private function getOrder() { - $message = __('Authorized amount of %1.', $this->amount); - - $this->payment->expects(static::once()) - ->method('getIsTransactionPending') - ->willReturn(false); - $this->payment->expects(static::once()) - ->method('getIsFraudDetected') - ->willReturn(false); - - $this->order->expects(static::once()) - ->method('getConfig') - ->willReturn($this->config); - $this->config->expects(static::once()) - ->method('getStateDefaultStatus') - ->with(Order::STATE_PROCESSING) - ->willReturn(Order::STATE_PROCESSING); - - $this->order->expects(static::once()) - ->method('setState') - ->with(Order::STATE_PROCESSING) - ->willReturnSelf(); - $this->order->expects(static::once()) - ->method('setStatus') - ->with(Order::STATE_PROCESSING); + $order = $this->getMockBuilder(Order::class) + ->disableOriginalConstructor() + ->getMock(); + $order->method('getBaseCurrency') + ->willReturn($this->getCurrency()); - $actual = $this->command->execute($this->payment, $this->amount, $this->order); - static::assertEquals($message, $actual); + return $order; } /** - * @covers \Magento\Sales\Model\Order\Payment\State\AuthorizeCommand::execute + * @param bool $isTransactionPending + * @param bool $isFraudDetected + * @return OrderPaymentInterface|MockObject */ - public function testExecutePendingTransaction() + private function getPayment($isTransactionPending, $isFraudDetected) { - $message = __('We will authorize %1 after the payment is approved at the payment gateway.', $this->amount); - - $this->payment->expects(static::once()) - ->method('getIsTransactionPending') - ->willReturn(true); - $this->payment->expects(static::once()) - ->method('getIsFraudDetected') - ->willReturn(false); - - $this->order->expects(static::once()) - ->method('getConfig') - ->willReturn($this->config); - $this->config->expects(static::once()) - ->method('getStateDefaultStatus') - ->with(Order::STATE_PAYMENT_REVIEW) - ->willReturn(Order::STATE_PAYMENT_REVIEW); - - $this->order->expects(static::once()) - ->method('setState') - ->with(Order::STATE_PAYMENT_REVIEW) - ->willReturnSelf(); - $this->order->expects(static::once()) - ->method('setStatus') - ->with(Order::STATE_PAYMENT_REVIEW); + $payment = $this->getMockBuilder(OrderPaymentInterface::class) + ->setMethods(['getIsTransactionPending', 'getIsFraudDetected']) + ->getMockForAbstractClass(); + $payment->method('getIsTransactionPending') + ->willReturn($isTransactionPending); + $payment->method('getIsFraudDetected') + ->willReturn($isFraudDetected); - $actual = $this->command->execute($this->payment, $this->amount, $this->order); - static::assertEquals($message, $actual); + return $payment; } /** - * @covers \Magento\Sales\Model\Order\Payment\State\AuthorizeCommand::execute + * @return Currency|MockObject */ - public function testExecutePendingTransactionFraud() + private function getCurrency() { - $expectedMessage = 'We will authorize %1 after the payment is approved at the payment gateway. '; - $expectedMessage .= 'Order is suspended as its authorizing amount %1 is suspected to be fraudulent.'; - $message = __($expectedMessage, $this->amount); - - $this->payment->expects(static::once()) - ->method('getIsTransactionPending') - ->willReturn(true); - $this->payment->expects(static::once()) - ->method('getIsFraudDetected') - ->willReturn(true); - - $this->order->expects(static::never()) - ->method('getConfig'); - - $this->order->expects(static::once()) - ->method('setState') - ->with(Order::STATE_PAYMENT_REVIEW) - ->willReturnSelf(); - $this->order->expects(static::once()) - ->method('setStatus') - ->with(Order::STATUS_FRAUD); + $currency = $this->getMockBuilder(Currency::class) + ->disableOriginalConstructor() + ->getMock(); + $currency->method('formatTxt') + ->willReturn($this->amount); - $actual = $this->command->execute($this->payment, $this->amount, $this->order); - static::assertEquals($message, $actual); + return $currency; } /** - * @covers \Magento\Sales\Model\Order\Payment\State\AuthorizeCommand::execute + * @param Order|MockObject $order + * @param string $expectedState + * @param string $expectedStatus */ - public function testExecuteFraud() + private function assertOrderStateAndStatus($order, $expectedState, $expectedStatus) { - $message = __( - 'Authorized amount of %1. Order is suspended as its authorizing amount %1 is suspected to be fraudulent.', - $this->amount - ); - - $this->payment->expects(static::once()) - ->method('getIsTransactionPending') - ->willReturn(false); - $this->payment->expects(static::once()) - ->method('getIsFraudDetected') - ->willReturn(true); - - $this->order->expects(static::never()) - ->method('getConfig'); - - $this->order->expects(static::once()) - ->method('setState') - ->with(Order::STATE_PAYMENT_REVIEW) - ->willReturnSelf(); - $this->order->expects(static::once()) - ->method('setStatus') - ->with(Order::STATUS_FRAUD); - - $actual = $this->command->execute($this->payment, $this->amount, $this->order); - static::assertEquals($message, $actual); + $order->method('setState')->with($expectedState); + $order->method('setStatus')->with($expectedStatus); } } diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/CaptureCommandTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/CaptureCommandTest.php index 6bae37f8ef8d7..d4c48d841261d 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/CaptureCommandTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/CaptureCommandTest.php @@ -6,206 +6,162 @@ namespace Magento\Sales\Test\Unit\Model\Order\Payment\State; use Magento\Directory\Model\Currency; +use Magento\Sales\Api\Data\OrderPaymentInterface; use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Config; -use Magento\Sales\Model\Order\Payment; use Magento\Sales\Model\Order\Payment\State\CaptureCommand; +use Magento\Sales\Model\Order\StatusResolver; use PHPUnit_Framework_MockObject_MockObject as MockObject; /** - * Class CaptureCommandTest + * @see CaptureCommand */ class CaptureCommandTest extends \PHPUnit_Framework_TestCase { /** - * @var Payment|MockObject + * @var float */ - private $payment; + private $amount = 10.00; /** - * @var Order|MockObject + * @var string */ - private $order; + private $newOrderStatus = 'custom_status'; /** - * @var Currency|MockObject + * @see CaptureCommand::execute + * + * @param bool $isTransactionPending + * @param bool $isFraudDetected + * @param string $expectedState + * @param string $expectedStatus + * @param string $expectedMessage + * + * @dataProvider commandResultDataProvider */ - private $currency; - - /** - * @var Config|MockObject - */ - private $config; + public function testExecute( + $isTransactionPending, + $isFraudDetected, + $expectedState, + $expectedStatus, + $expectedMessage + ) { + $actualReturn = (new CaptureCommand($this->getStatusResolver()))->execute( + $this->getPayment($isTransactionPending, $isFraudDetected), + $this->amount, + $this->getOrder() + ); + + $this->assertOrderStateAndStatus($this->getOrder(), $expectedState, $expectedStatus); + self::assertEquals(__($expectedMessage, $this->amount), $actualReturn); + } /** - * @var CaptureCommand + * @return array */ - private $command; + public function commandResultDataProvider() + { + return [ + [ + false, + false, + Order::STATE_PROCESSING, + $this->newOrderStatus, + 'Captured amount of %1 online.' + ], + [ + true, + false, + Order::STATE_PAYMENT_REVIEW, + $this->newOrderStatus, + 'An amount of %1 will be captured after being approved at the payment gateway.' + ], + [ + false, + true, + Order::STATE_PAYMENT_REVIEW, + Order::STATUS_FRAUD, + 'Captured amount of %1 online.' . + ' Order is suspended as its capturing amount %1 is suspected to be fraudulent.' + ], + [ + true, + true, + Order::STATE_PAYMENT_REVIEW, + Order::STATUS_FRAUD, + 'An amount of %1 will be captured after being approved at the payment gateway.' . + ' Order is suspended as its capturing amount %1 is suspected to be fraudulent.' + ], + ]; + } /** - * @var int + * @return StatusResolver|MockObject */ - private $amount = 45; - - protected function setUp() + private function getStatusResolver() { - $this->currency = $this->getMockBuilder(Currency::class) - ->disableOriginalConstructor() - ->setMethods(['formatTxt']) - ->getMock(); - - $this->config = $this->getMockBuilder(Config::class) + $statusResolver = $this->getMockBuilder(StatusResolver::class) ->disableOriginalConstructor() - ->setMethods(['getStateDefaultStatus']) ->getMock(); + $statusResolver->method('getOrderStatusByState') + ->willReturn($this->newOrderStatus); - $this->payment = $this->getMockBuilder(Payment::class) - ->disableOriginalConstructor() - ->setMethods(['getIsTransactionPending', 'getIsFraudDetected']) - ->getMock(); - $this->order = $this->getMockBuilder(Order::class) - ->disableOriginalConstructor() - ->setMethods(['getBaseCurrency', 'getConfig', 'setState', 'setStatus']) - ->getMock(); - - $this->order->expects(static::once()) - ->method('getBaseCurrency') - ->willReturn($this->currency); - $this->currency->expects(static::once()) - ->method('formatTxt') - ->with($this->amount) - ->willReturn($this->amount); - - $this->command = new CaptureCommand(); + return $statusResolver; } /** - * @covers \Magento\Sales\Model\Order\Payment\State\CaptureCommand::execute + * @return Order|MockObject */ - public function testExecute() + private function getOrder() { - $message = __('Captured amount of %1 online.', $this->amount); - - $this->payment->expects(static::once()) - ->method('getIsTransactionPending') - ->willReturn(false); - $this->payment->expects(static::once()) - ->method('getIsFraudDetected') - ->willReturn(false); - - $this->order->expects(static::once()) - ->method('getConfig') - ->willReturn($this->config); - $this->config->expects(static::once()) - ->method('getStateDefaultStatus') - ->with(Order::STATE_PROCESSING) - ->willReturn(Order::STATE_PROCESSING); - - $this->order->expects(static::once()) - ->method('setState') - ->with(Order::STATE_PROCESSING) - ->willReturnSelf(); - $this->order->expects(static::once()) - ->method('setStatus') - ->with(Order::STATE_PROCESSING); + $order = $this->getMockBuilder(Order::class) + ->disableOriginalConstructor() + ->getMock(); + $order->method('getBaseCurrency') + ->willReturn($this->getCurrency()); - $actual = $this->command->execute($this->payment, $this->amount, $this->order); - static::assertEquals($message, $actual); + return $order; } /** - * @covers \Magento\Sales\Model\Order\Payment\State\CaptureCommand::execute + * @param bool $isTransactionPending + * @param bool $isFraudDetected + * @return OrderPaymentInterface|MockObject */ - public function testExecutePendingTransaction() + private function getPayment($isTransactionPending, $isFraudDetected) { - $message = __('An amount of %1 will be captured after being approved at the payment gateway.', $this->amount); - - $this->payment->expects(static::once()) - ->method('getIsTransactionPending') - ->willReturn(true); - $this->payment->expects(static::once()) - ->method('getIsFraudDetected') - ->willReturn(false); - - $this->order->expects(static::once()) - ->method('getConfig') - ->willReturn($this->config); - $this->config->expects(static::once()) - ->method('getStateDefaultStatus') - ->with(Order::STATE_PAYMENT_REVIEW) - ->willReturn(Order::STATE_PAYMENT_REVIEW); - - $this->order->expects(static::once()) - ->method('setState') - ->with(Order::STATE_PAYMENT_REVIEW) - ->willReturnSelf(); - $this->order->expects(static::once()) - ->method('setStatus') - ->with(Order::STATE_PAYMENT_REVIEW); + $payment = $this->getMockBuilder(OrderPaymentInterface::class) + ->setMethods(['getIsTransactionPending', 'getIsFraudDetected']) + ->getMockForAbstractClass(); + $payment->method('getIsTransactionPending') + ->willReturn($isTransactionPending); + $payment->method('getIsFraudDetected') + ->willReturn($isFraudDetected); - $actual = $this->command->execute($this->payment, $this->amount, $this->order); - static::assertEquals($message, $actual); + return $payment; } /** - * @covers \Magento\Sales\Model\Order\Payment\State\CaptureCommand::execute + * @return Currency|MockObject */ - public function testExecutePendingTransactionFraud() + private function getCurrency() { - $expectedMessage = 'An amount of %1 will be captured after being approved at the payment gateway. '; - $expectedMessage .= 'Order is suspended as its capturing amount %1 is suspected to be fraudulent.'; - $message = __($expectedMessage, $this->amount); - - $this->payment->expects(static::once()) - ->method('getIsTransactionPending') - ->willReturn(true); - $this->payment->expects(static::once()) - ->method('getIsFraudDetected') - ->willReturn(true); - - $this->order->expects(static::never()) - ->method('getConfig'); - - $this->order->expects(static::once()) - ->method('setState') - ->with(Order::STATE_PAYMENT_REVIEW) - ->willReturnSelf(); - $this->order->expects(static::once()) - ->method('setStatus') - ->with(Order::STATUS_FRAUD); + $currency = $this->getMockBuilder(Currency::class) + ->disableOriginalConstructor() + ->getMock(); + $currency->method('formatTxt') + ->willReturn($this->amount); - $actual = $this->command->execute($this->payment, $this->amount, $this->order); - static::assertEquals($message, $actual); + return $currency; } /** - * @covers \Magento\Sales\Model\Order\Payment\State\CaptureCommand::execute + * @param Order|MockObject $order + * @param string $expectedState + * @param string $expectedStatus */ - public function testExecuteFraud() + private function assertOrderStateAndStatus($order, $expectedState, $expectedStatus) { - $expectedMessage = 'Captured amount of %1 online. '; - $expectedMessage .= 'Order is suspended as its capturing amount %1 is suspected to be fraudulent.'; - $message = __($expectedMessage, $this->amount); - - $this->payment->expects(static::once()) - ->method('getIsTransactionPending') - ->willReturn(false); - $this->payment->expects(static::once()) - ->method('getIsFraudDetected') - ->willReturn(true); - - $this->order->expects(static::never()) - ->method('getConfig'); - - $this->order->expects(static::once()) - ->method('setState') - ->with(Order::STATE_PAYMENT_REVIEW) - ->willReturnSelf(); - $this->order->expects(static::once()) - ->method('setStatus') - ->with(Order::STATUS_FRAUD); - - $actual = $this->command->execute($this->payment, $this->amount, $this->order); - static::assertEquals($message, $actual); + $order->method('setState')->with($expectedState); + $order->method('setStatus')->with($expectedStatus); } } diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/OrderCommandTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/OrderCommandTest.php new file mode 100644 index 0000000000000..ba5700e9028d7 --- /dev/null +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/OrderCommandTest.php @@ -0,0 +1,165 @@ +getStatusResolver()))->execute( + $this->getPayment($isTransactionPending, $isFraudDetected), + $this->amount, + $this->getOrder() + ); + + $this->assertOrderStateAndStatus($this->getOrder(), $expectedState, $expectedStatus); + self::assertEquals(__($expectedMessage, $this->amount), $actualReturn); + } + + /** + * @return array + */ + public function commandResultDataProvider() + { + return [ + [ + false, + false, + Order::STATE_PROCESSING, + $this->newOrderStatus, + 'Ordered amount of %1' + ], + [ + true, + false, + Order::STATE_PAYMENT_REVIEW, + $this->newOrderStatus, + 'The order amount of %1 is pending approval on the payment gateway.' + ], + [ + false, + true, + Order::STATE_PAYMENT_REVIEW, + Order::STATUS_FRAUD, + 'The order amount of %1 is pending approval on the payment gateway.' + ], + [ + true, + true, + Order::STATE_PAYMENT_REVIEW, + Order::STATUS_FRAUD, + 'The order amount of %1 is pending approval on the payment gateway.' + ], + ]; + } + + /** + * @return StatusResolver|MockObject + */ + private function getStatusResolver() + { + $statusResolver = $this->getMockBuilder(StatusResolver::class) + ->disableOriginalConstructor() + ->getMock(); + $statusResolver->method('getOrderStatusByState') + ->willReturn($this->newOrderStatus); + + return $statusResolver; + } + + /** + * @return Order|MockObject + */ + private function getOrder() + { + $order = $this->getMockBuilder(Order::class) + ->disableOriginalConstructor() + ->getMock(); + $order->method('getBaseCurrency') + ->willReturn($this->getCurrency()); + + return $order; + } + + /** + * @param bool $isTransactionPending + * @param bool $isFraudDetected + * @return OrderPaymentInterface|MockObject + */ + private function getPayment($isTransactionPending, $isFraudDetected) + { + $payment = $this->getMockBuilder(OrderPaymentInterface::class) + ->setMethods(['getIsTransactionPending', 'getIsFraudDetected']) + ->getMockForAbstractClass(); + $payment->method('getIsTransactionPending') + ->willReturn($isTransactionPending); + $payment->method('getIsFraudDetected') + ->willReturn($isFraudDetected); + + return $payment; + } + + /** + * @return Currency|MockObject + */ + private function getCurrency() + { + $currency = $this->getMockBuilder(Currency::class) + ->disableOriginalConstructor() + ->getMock(); + $currency->method('formatTxt') + ->willReturn($this->amount); + + return $currency; + } + + /** + * @param Order|MockObject $order + * @param string $expectedState + * @param string $expectedStatus + */ + private function assertOrderStateAndStatus($order, $expectedState, $expectedStatus) + { + $order->method('setState')->with($expectedState); + $order->method('setStatus')->with($expectedStatus); + } +} diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/RegisterCaptureNotificationCommandTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/RegisterCaptureNotificationCommandTest.php new file mode 100644 index 0000000000000..1a5ba3e881222 --- /dev/null +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/RegisterCaptureNotificationCommandTest.php @@ -0,0 +1,165 @@ +getStatusResolver()))->execute( + $this->getPayment($isTransactionPending, $isFraudDetected), + $this->amount, + $this->getOrder() + ); + + $this->assertOrderStateAndStatus($this->getOrder(), $expectedState, $expectedStatus); + self::assertEquals(__($expectedMessage, $this->amount), $actualReturn); + } + + /** + * @return array + */ + public function commandResultDataProvider() + { + return [ + [ + false, + false, + Order::STATE_PROCESSING, + $this->newOrderStatus, + 'Registered notification about captured amount of %1.' + ], + [ + true, + false, + Order::STATE_PAYMENT_REVIEW, + $this->newOrderStatus, + 'An amount of %1 will be captured after being approved at the payment gateway.' + ], + [ + false, + true, + Order::STATE_PAYMENT_REVIEW, + Order::STATUS_FRAUD, + 'Order is suspended as its capture amount %1 is suspected to be fraudulent.' + ], + [ + true, + true, + Order::STATE_PAYMENT_REVIEW, + Order::STATUS_FRAUD, + 'Order is suspended as its capture amount %1 is suspected to be fraudulent.' + ], + ]; + } + + /** + * @return StatusResolver|MockObject + */ + private function getStatusResolver() + { + $statusResolver = $this->getMockBuilder(StatusResolver::class) + ->disableOriginalConstructor() + ->getMock(); + $statusResolver->method('getOrderStatusByState') + ->willReturn($this->newOrderStatus); + + return $statusResolver; + } + + /** + * @return Order|MockObject + */ + private function getOrder() + { + $order = $this->getMockBuilder(Order::class) + ->disableOriginalConstructor() + ->getMock(); + $order->method('getBaseCurrency') + ->willReturn($this->getCurrency()); + + return $order; + } + + /** + * @param bool $isTransactionPending + * @param bool $isFraudDetected + * @return OrderPaymentInterface|MockObject + */ + private function getPayment($isTransactionPending, $isFraudDetected) + { + $payment = $this->getMockBuilder(OrderPaymentInterface::class) + ->setMethods(['getIsTransactionPending', 'getIsFraudDetected']) + ->getMockForAbstractClass(); + $payment->method('getIsTransactionPending') + ->willReturn($isTransactionPending); + $payment->method('getIsFraudDetected') + ->willReturn($isFraudDetected); + + return $payment; + } + + /** + * @return Currency|MockObject + */ + private function getCurrency() + { + $currency = $this->getMockBuilder(Currency::class) + ->disableOriginalConstructor() + ->getMock(); + $currency->method('formatTxt') + ->willReturn($this->amount); + + return $currency; + } + + /** + * @param Order|MockObject $order + * @param string $expectedState + * @param string $expectedStatus + */ + private function assertOrderStateAndStatus($order, $expectedState, $expectedStatus) + { + $order->method('setState')->with($expectedState); + $order->method('setStatus')->with($expectedStatus); + } +} diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/StatusResolverTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/StatusResolverTest.php new file mode 100644 index 0000000000000..af6535fae2c7a --- /dev/null +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/StatusResolverTest.php @@ -0,0 +1,108 @@ +getOrderStatusByState($order, 'new'); + + self::assertEquals($expectedReturn, $actualReturn); + } + + public function statesDataProvider() + { + return [ + [ + $this->getOrder('pending', ['pending' => 'pending']), + 'pending' + ], + [ + $this->getOrder('processing', ['pending' => 'pending']), + 'processing' + ], + ]; + } + + /** + * @param string $newOrderStatus + * @param array $stateStatuses + * @return OrderInterface|MockObject + */ + private function getOrder($newOrderStatus, $stateStatuses) + { + $order = $this->getMockBuilder(OrderInterface::class) + ->setMethods(['getConfig']) + ->getMockForAbstractClass(); + $order->method('getPayment') + ->willReturn($this->getPayment($newOrderStatus)); + $order->method('getConfig') + ->willReturn($this->getConfig($stateStatuses)); + + return $order; + } + + /** + * @param string $newOrderStatus + * @return MockObject + */ + private function getPayment($newOrderStatus) + { + $payment = $this->getMockBuilder(OrderPaymentInterface::class) + ->setMethods(['getMethodInstance']) + ->getMockForAbstractClass(); + $payment->method('getMethodInstance') + ->willReturn($this->getMethodInstance($newOrderStatus)); + + return $payment; + } + + /** + * @param string $newOrderStatus + * @return MethodInterface|MockObject + */ + private function getMethodInstance($newOrderStatus) + { + $methodInstance = $this->getMockBuilder(MethodInterface::class) + ->getMockForAbstractClass(); + $methodInstance->method('getConfigData') + ->with('order_status') + ->willReturn($newOrderStatus); + + return $methodInstance; + } + + /** + * @param array $stateStatuses + * @return Config|MockObject + */ + private function getConfig($stateStatuses) + { + $config = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + $config->method('getStateStatuses') + ->willReturn($stateStatuses); + $config->method('getStateDefaultStatus') + ->willReturn('processing'); + + return $config; + } +} From 6de30cd7ea62d8ddba7c836000f1042883f52888 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi Date: Fri, 16 Jun 2017 16:19:19 +0300 Subject: [PATCH 21/39] MAGETWO-69112: No request sent to Authorize.net after reordering order in admin - Add variation to functional test --- .../Test/TestCase/ReorderOrderEntityTest.xml | 33 ++++++ .../Test/TestCase/ReorderOrderEntityTest.php | 2 + .../Test/TestCase/ReorderOrderEntityTest.xml | 3 +- .../SubmitOrderWithoutAdditionalInfoStep.php | 111 ++++++++++++++++++ .../app/Magento/Sales/Test/etc/testcase.xml | 8 +- 5 files changed, 151 insertions(+), 6 deletions(-) create mode 100644 dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/ReorderOrderEntityTest.xml create mode 100644 dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/SubmitOrderWithoutAdditionalInfoStep.php diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/ReorderOrderEntityTest.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/ReorderOrderEntityTest.xml new file mode 100644 index 0000000000000..b0f4856e28d0d --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/ReorderOrderEntityTest.xml @@ -0,0 +1,33 @@ + + + + + + test_type:3rd_party_test, severity:S2 + Reorder placed order using Authorize.Net payment method (update products, billing address). + default + default + US_address_1_without_email + Flat Rate + Fixed + + 565.00 + + authorizenet_directpost + authorizenet + visa_default_admin + Processing + Processing + Back, Reorder, Cancel, Send Email, Hold, Invoice, Ship, Edit + + + + + + + diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/ReorderOrderEntityTest.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/ReorderOrderEntityTest.php index 84ce3f52428f5..cd05486ae98cc 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/ReorderOrderEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/ReorderOrderEntityTest.php @@ -28,6 +28,8 @@ class ReorderOrderEntityTest extends Scenario { /* tags */ const MVP = 'yes'; + const TEST_TYPE = '3rd_party_test'; + const SEVERITY = 'S2'; /* end tags */ /** diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/ReorderOrderEntityTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/ReorderOrderEntityTest.xml index ab912cb4cec46..8b98497f3368c 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/ReorderOrderEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/ReorderOrderEntityTest.xml @@ -8,7 +8,6 @@ - to_maintain:yes Reorder placed order (update products, billing address). two_simple_product active_sales_rule_with_fixed_price_discount_coupon @@ -17,7 +16,7 @@ Flat Rate Fixed - 1,030.00 + 1030.00 checkmo Pending diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/SubmitOrderWithoutAdditionalInfoStep.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/SubmitOrderWithoutAdditionalInfoStep.php new file mode 100644 index 0000000000000..6e51affdc6ee5 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/SubmitOrderWithoutAdditionalInfoStep.php @@ -0,0 +1,111 @@ +orderCreateIndex = $orderCreateIndex; + $this->salesOrderView = $salesOrderView; + $this->fixtureFactory = $fixtureFactory; + $this->customer = $customer; + $this->billingAddress = $billingAddress; + $this->products = $products; + } + + /** + * Fill Sales Data. + * + * @return array + */ + public function run() + { + $this->orderCreateIndex->getCreateBlock()->submitOrder(); + $this->salesOrderView->getMessagesBlock()->waitSuccessMessage(); + $orderId = trim($this->salesOrderView->getTitleBlock()->getTitle(), '#'); + $order = $this->fixtureFactory->createByCode( + 'orderInjectable', + [ + 'data' => [ + 'id' => $orderId, + 'customer_id' => ['customer' => $this->customer], + 'entity_id' => ['products' => $this->products], + 'billing_address_id' => ['billingAddress' => $this->billingAddress], + ] + ] + ); + + return ['orderId' => $orderId, 'order' => $order]; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/etc/testcase.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/etc/testcase.xml index 66e3703ff538a..53f7eed10407d 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/etc/testcase.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/etc/testcase.xml @@ -12,10 +12,10 @@ - - - - + + + + From f24878a54abfa54967f002c8be6ac86bdfb49b65 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi Date: Mon, 19 Jun 2017 14:54:10 +0300 Subject: [PATCH 22/39] MAGETWO-67632: [Github] Invalid method usage in PayPal NVP callDoReauthorization method --- app/code/Magento/Paypal/Model/Api/Nvp.php | 6 +-- .../Paypal/Test/Unit/Model/Api/NvpTest.php | 37 +++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Paypal/Model/Api/Nvp.php b/app/code/Magento/Paypal/Model/Api/Nvp.php index 7a77279500d79..6bc2ff9678a55 100644 --- a/app/code/Magento/Paypal/Model/Api/Nvp.php +++ b/app/code/Magento/Paypal/Model/Api/Nvp.php @@ -914,15 +914,15 @@ public function getIsFraudDetected() } /** - * Made additional request to paypal to get autharization id + * Made additional request to PayPal to get authorization id * * @return void */ public function callDoReauthorization() { - $request = $this->_export($this->_doReauthorizationRequest); + $request = $this->_exportToRequest($this->_doReauthorizationRequest); $response = $this->call('DoReauthorization', $request); - $this->_import($response, $this->_doReauthorizationResponse); + $this->_importFromResponse($this->_doReauthorizationResponse, $response); } /** diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Api/NvpTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Api/NvpTest.php index dfabe34d7b4b7..f031933f30215 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/Api/NvpTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/Api/NvpTest.php @@ -9,6 +9,7 @@ namespace Magento\Paypal\Test\Unit\Model\Api; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Paypal\Model\Info; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -211,6 +212,42 @@ public function testCallGetExpressCheckoutDetails() $this->assertEquals('testSTATE', $address->getRegion()); } + /** + * Tests that callDoReauthorization method is called without errors and + * needed data is imported from response. + */ + public function testCallDoReauthorization() + { + $authorizationId = 555; + $paymentStatus = 'Completed'; + $pendingReason = 'none'; + $protectionEligibility = 'Eligible'; + $protectionEligibilityType = 'ItemNotReceivedEligible'; + + $this->curl->expects($this->once()) + ->method('read') + ->willReturn( + "\r\n" . 'ACK=Success' + . '&AUTHORIZATIONID=' . $authorizationId + . '&PAYMENTSTATUS=' . $paymentStatus + . '&PENDINGREASON=' . $pendingReason + . '&PROTECTIONELIGIBILITY=' . $protectionEligibility + . '&PROTECTIONELIGIBILITYTYPE=' . $protectionEligibilityType + ); + + $this->model->callDoReauthorization(); + + $expectedImportedData = [ + 'authorization_id' => $authorizationId, + 'payment_status' => Info::PAYMENTSTATUS_COMPLETED, + 'pending_reason' => $pendingReason, + 'protection_eligibility' => $protectionEligibility + ]; + + $this->assertNotContains($protectionEligibilityType, $this->model->getData()); + $this->assertEquals($expectedImportedData, $this->model->getData()); + } + public function testGetDebugReplacePrivateDataKeys() { $debugReplacePrivateDataKeys = $this->_invokeNvpProperty($this->model, '_debugReplacePrivateDataKeys'); From 99e262d89f9812d344e15f4e4216f3d98bd21083 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi Date: Mon, 19 Jun 2017 18:08:52 +0300 Subject: [PATCH 23/39] MAGETWO-63239: [GITHUB] No possibility to save payment transaction details --- .../Magento/Payment/Model/Method/Adapter.php | 2 +- .../Test/Unit/Model/Method/AdapterTest.php | 42 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Payment/Model/Method/Adapter.php b/app/code/Magento/Payment/Model/Method/Adapter.php index 32c8eb797d9dd..9f0f946dc6a5b 100644 --- a/app/code/Magento/Payment/Model/Method/Adapter.php +++ b/app/code/Magento/Payment/Model/Method/Adapter.php @@ -419,7 +419,7 @@ public function validate() */ public function fetchTransactionInfo(InfoInterface $payment, $transactionId) { - $this->executeCommand( + return $this->executeCommand( 'fetch_transaction_information', ['payment' => $payment, 'transactionId' => $transactionId] ); diff --git a/app/code/Magento/Payment/Test/Unit/Model/Method/AdapterTest.php b/app/code/Magento/Payment/Test/Unit/Model/Method/AdapterTest.php index 5c73be0fe5256..c5d11bbf01e94 100644 --- a/app/code/Magento/Payment/Test/Unit/Model/Method/AdapterTest.php +++ b/app/code/Magento/Payment/Test/Unit/Model/Method/AdapterTest.php @@ -105,6 +105,48 @@ protected function setUp() ); } + public function testFetchTransactionInfo() + { + $transactionId = 10555; + $transactionInfo = ['test_key' => 'test_value']; + + $valueHandler = $this->getMockForAbstractClass(ValueHandlerInterface::class); + $command = $this->getMockForAbstractClass(CommandInterface::class); + + /** @var InfoInterface|MockObject $paymentInfo */ + $paymentInfo = $this->getMockForAbstractClass(InfoInterface::class); + $paymentDO = $this->getMockForAbstractClass(PaymentDataObjectInterface::class); + + $this->valueHandlerPool->expects(static::once()) + ->method('get') + ->with('can_fetch_transaction_information') + ->willReturn($valueHandler); + $valueHandler->expects(static::once()) + ->method('handle') + ->with(['field' => 'can_fetch_transaction_information']) + ->willReturn(true); + + $this->paymentDataObjectFactory->expects(static::once()) + ->method('create') + ->with($paymentInfo) + ->willReturn($paymentDO); + + $this->commandPool->expects(static::once()) + ->method('get') + ->with('fetch_transaction_information') + ->willReturn($command); + $command->expects(static::once()) + ->method('execute') + ->with(['transactionId' => $transactionId, 'payment' => $paymentDO]) + ->willReturn($transactionInfo); + + $this->assertEquals( + $transactionInfo, + $this->adapter->fetchTransactionInfo($paymentInfo, $transactionId) + ); + + } + /** * @covers \Magento\Payment\Model\Method\Adapter::isAvailable */ From a4cae96341ebc2160b0c957e0dc5d28ba0413917 Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov Date: Tue, 20 Jun 2017 16:12:38 +0300 Subject: [PATCH 24/39] MAGETWO-69121: Loader doesn't disappear if Authorize.net transact.dll fails request - Added stop loader for unsuccessful place order flow --- .../templates/transparent/iframe.phtml | 7 +++--- .../Test/Repository/ConfigData.xml | 8 ++++++ .../TestCase/OnePageCheckoutDeclinedTest.xml | 25 +++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/OnePageCheckoutDeclinedTest.xml diff --git a/app/code/Magento/Payment/view/frontend/templates/transparent/iframe.phtml b/app/code/Magento/Payment/view/frontend/templates/transparent/iframe.phtml index 153f9c5102920..d338c80f675bb 100644 --- a/app/code/Magento/Payment/view/frontend/templates/transparent/iframe.phtml +++ b/app/code/Magento/Payment/view/frontend/templates/transparent/iframe.phtml @@ -5,7 +5,6 @@ */ // @codingStandardsIgnoreFile -use Magento\Framework\Json\Helper\Data; /** @var \Magento\Payment\Block\Transparent\Iframe $block */ $params = $block->getParams(); @@ -33,11 +32,13 @@ $params = $block->getParams(); [ 'jquery', 'Magento_Ui/js/model/messageList', - 'mage/translate' + 'mage/translate', + 'Magento_Checkout/js/model/full-screen-loader' ], - function($, globalMessageList, $t) { + function($, globalMessageList, $t, fullScreenLoader) { var parent = window.top; $(parent).trigger('clearTimeout'); + fullScreenLoader.stopLoader(); globalMessageList.addErrorMessage({ message: $t('An error occurred on the server. Please try to place the order again.') }); diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Repository/ConfigData.xml index a1f0d93e48460..c759b537a191d 100644 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Repository/ConfigData.xml +++ b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Repository/ConfigData.xml @@ -151,5 +151,13 @@ authorize + + + payment + 1 + + + + diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/OnePageCheckoutDeclinedTest.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/OnePageCheckoutDeclinedTest.xml new file mode 100644 index 0000000000000..1c589afcc1e20 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/OnePageCheckoutDeclinedTest.xml @@ -0,0 +1,25 @@ + + + + + + catalogProductSimple::product_10_dollar + default + US_address_1_without_email + login + Flat Rate + Fixed + authorizenet_directpost + visa_default + authorizenet, authorizenet_wrong_credentials + An error occurred on the server. Please try to place the order again. + test_type:3rd_party_test, severity:S2 + + + + From f7940702db7971eed68846994ec067d8d7b318a4 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi Date: Wed, 21 Jun 2017 10:55:28 +0300 Subject: [PATCH 25/39] MAGETWO-63239: [GITHUB] No possibility to save payment transaction details --- .../Payment/Test/Unit/Model/Method/AdapterTest.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Payment/Test/Unit/Model/Method/AdapterTest.php b/app/code/Magento/Payment/Test/Unit/Model/Method/AdapterTest.php index c5d11bbf01e94..0961095a236c7 100644 --- a/app/code/Magento/Payment/Test/Unit/Model/Method/AdapterTest.php +++ b/app/code/Magento/Payment/Test/Unit/Model/Method/AdapterTest.php @@ -117,25 +117,22 @@ public function testFetchTransactionInfo() $paymentInfo = $this->getMockForAbstractClass(InfoInterface::class); $paymentDO = $this->getMockForAbstractClass(PaymentDataObjectInterface::class); - $this->valueHandlerPool->expects(static::once()) - ->method('get') + $this->valueHandlerPool->method('get') ->with('can_fetch_transaction_information') ->willReturn($valueHandler); - $valueHandler->expects(static::once()) + $valueHandler->expects($this->atLeastOnce()) ->method('handle') ->with(['field' => 'can_fetch_transaction_information']) ->willReturn(true); - $this->paymentDataObjectFactory->expects(static::once()) - ->method('create') + $this->paymentDataObjectFactory->method('create') ->with($paymentInfo) ->willReturn($paymentDO); - $this->commandPool->expects(static::once()) - ->method('get') + $this->commandPool->method('get') ->with('fetch_transaction_information') ->willReturn($command); - $command->expects(static::once()) + $command->expects($this->atLeastOnce()) ->method('execute') ->with(['transactionId' => $transactionId, 'payment' => $paymentDO]) ->willReturn($transactionInfo); From 944de8d957c5d165c6d96e5c1a8b0b8076951256 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov Date: Wed, 21 Jun 2017 12:21:08 +0300 Subject: [PATCH 26/39] MAGETWO-64518: \Magento\CatalogRule\Model\Indexer\IndexBuilder method "doReindexFull()" causes temporary missing sale prices --- .../Indexer/Category/Product/Action/Full.php | 2 +- .../Model/Indexer/Product/Eav/Action/Full.php | 2 +- .../Indexer/Product/Price/Action/Full.php | 2 +- .../Indexer/ActiveTableSwitcher.php | 22 +- .../Indexer/ActiveTableSwitcherTest.php | 2 +- .../Model/Indexer/Stock/Action/Full.php | 2 +- .../Model/Indexer/IndexBuilder.php | 434 +++++------------- .../Model/Indexer/ProductPriceCalculator.php | 57 +++ .../Model/Indexer/ReindexRuleGroupWebsite.php | 79 ++++ .../Model/Indexer/ReindexRuleProduct.php | 110 +++++ .../Model/Indexer/ReindexRuleProductPrice.php | 163 +++++++ .../Indexer/RuleProductPricesPersistor.php | 76 +++ .../Indexer/RuleProductsSelectBuilder.php | 150 ++++++ app/code/Magento/CatalogRule/Model/Rule.php | 12 +- .../CatalogRule/Setup/UpgradeSchema.php | 43 ++ .../Unit/Model/Indexer/IndexBuilderTest.php | 38 +- ...ndleWithCatalogPriceRuleCalculatorTest.php | 4 +- ...dle_product_with_catalog_rule_rollback.php | 1 + ...dle_product_with_catalog_rule_rollback.php | 1 + .../Model/Indexer/BatchIndexTest.php | 31 +- .../Model/Indexer/IndexerBuilderTest.php | 25 +- .../Model/Indexer/RuleProductTest.php | 9 +- .../CatalogRule/_files/attribute_rollback.php | 20 + ...atalog_rule_10_off_not_logged_rollback.php | 28 ++ .../_files/rule_by_attribute_rollback.php | 27 ++ .../CatalogRule/_files/two_rules_rollback.php | 30 ++ 26 files changed, 1013 insertions(+), 357 deletions(-) create mode 100644 app/code/Magento/CatalogRule/Model/Indexer/ProductPriceCalculator.php create mode 100644 app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleGroupWebsite.php create mode 100644 app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php create mode 100644 app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php create mode 100644 app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php create mode 100644 app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/attribute_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_by_attribute_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/two_rules_rollback.php diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php index e023d4b3ce819..ae0c3554c0d32 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php @@ -95,7 +95,7 @@ public function __construct( public function execute() { $this->reindex(); - $this->activeTableSwitcher->switchTable($this->connection, $this->getMainTable()); + $this->activeTableSwitcher->switchTable($this->connection, [$this->getMainTable()]); return $this; } diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php index e966c5653fde7..bc747e62f641e 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php @@ -97,7 +97,7 @@ public function execute($ids = null) $this->syncData($indexer, $mainTable); } } - $this->activeTableSwitcher->switchTable($indexer->getConnection(), $indexer->getMainTable()); + $this->activeTableSwitcher->switchTable($indexer->getConnection(), [$indexer->getMainTable()]); } } catch (\Exception $e) { throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()), $e); diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php index 5d2b408665bad..88dba03def001 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php @@ -150,7 +150,7 @@ public function execute($ids = null) } $this->activeTableSwitcher->switchTable( $this->_defaultIndexerResource->getConnection(), - $this->_defaultIndexerResource->getMainTable() + [$this->_defaultIndexerResource->getMainTable()] ); } catch (\Exception $e) { throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()), $e); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Indexer/ActiveTableSwitcher.php b/app/code/Magento/Catalog/Model/ResourceModel/Indexer/ActiveTableSwitcher.php index 234c0823f47da..6d3451f2d90f8 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Indexer/ActiveTableSwitcher.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Indexer/ActiveTableSwitcher.php @@ -20,16 +20,17 @@ class ActiveTableSwitcher * Switch index tables from replica to active. * * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection - * @param string $tableName + * @param array $tableNames * @return void */ - public function switchTable(\Magento\Framework\DB\Adapter\AdapterInterface $connection, $tableName) + public function switchTable(\Magento\Framework\DB\Adapter\AdapterInterface $connection, array $tableNames) { - $outdatedTableName = $tableName . $this->outdatedTableSuffix; - $replicaTableName = $tableName . $this->additionalTableSuffix; + $toRename = []; + foreach ($tableNames as $tableName) { + $outdatedTableName = $tableName . $this->outdatedTableSuffix; + $replicaTableName = $tableName . $this->additionalTableSuffix; - $connection->renameTablesBatch( - [ + $renameBatch = [ [ 'oldName' => $tableName, 'newName' => $outdatedTableName @@ -42,8 +43,13 @@ public function switchTable(\Magento\Framework\DB\Adapter\AdapterInterface $conn 'oldName' => $outdatedTableName, 'newName' => $replicaTableName ] - ] - ); + ]; + $toRename = array_merge($toRename, $renameBatch); + } + + if (!empty($toRename)) { + $connection->renameTablesBatch($toRename); + } } /** diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Indexer/ActiveTableSwitcherTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Indexer/ActiveTableSwitcherTest.php index 3920a0d10370e..3f4e4f1f05996 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Indexer/ActiveTableSwitcherTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Indexer/ActiveTableSwitcherTest.php @@ -42,7 +42,7 @@ public function testSwitch() ] ); - $this->model->switchTable($connectionMock, $tableName); + $this->model->switchTable($connectionMock, [$tableName]); } public function testGetAdditionalTableName() diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/Stock/Action/Full.php b/app/code/Magento/CatalogInventory/Model/Indexer/Stock/Action/Full.php index 24281dd1a0c50..793a122b996d0 100644 --- a/app/code/Magento/CatalogInventory/Model/Indexer/Stock/Action/Full.php +++ b/app/code/Magento/CatalogInventory/Model/Indexer/Stock/Action/Full.php @@ -156,7 +156,7 @@ public function execute($ids = null) } } } - $this->activeTableSwitcher->switchTable($indexer->getConnection(), $indexer->getMainTable()); + $this->activeTableSwitcher->switchTable($indexer->getConnection(), [$indexer->getMainTable()]); } catch (\Exception $e) { throw new LocalizedException(__($e->getMessage()), $e); } diff --git a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php index 53c7544f70e79..6c9a4ecdce1cb 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php @@ -6,17 +6,17 @@ namespace Magento\CatalogRule\Model\Indexer; -use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product; use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory as RuleCollectionFactory; use Magento\CatalogRule\Model\Rule; -use Magento\Framework\App\ResourceConnection; -use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Pricing\PriceCurrencyInterface; /** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @api + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.TooManyFields) */ class IndexBuilder { @@ -24,6 +24,7 @@ class IndexBuilder /** * @var \Magento\Framework\EntityManager\MetadataPool + * @deprecated */ protected $metadataPool; @@ -33,6 +34,7 @@ class IndexBuilder * This array contain list of CatalogRuleGroupWebsite table columns * * @var array + * @deprecated */ protected $_catalogRuleGroupWebsiteColumnsList = ['rule_id', 'customer_group_id', 'website_id']; @@ -96,6 +98,41 @@ class IndexBuilder */ protected $connection; + /** + * @var \Magento\CatalogRule\Model\Indexer\ProductPriceCalculator + */ + private $productPriceCalculator; + + /** + * @var \Magento\CatalogRule\Model\Indexer\ReindexRuleProduct + */ + private $reindexRuleProduct; + + /** + * @var \Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite + */ + private $reindexRuleGroupWebsite; + + /** + * @var \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder + */ + private $ruleProductsSelectBuilder; + + /** + * @var \Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice + */ + private $reindexRuleProductPrice; + + /** + * @var \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor + */ + private $pricesPersistor; + + /** + * @var \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher + */ + private $activeTableSwitcher; + /** * @param RuleCollectionFactory $ruleCollectionFactory * @param PriceCurrencyInterface $priceCurrency @@ -107,6 +144,13 @@ class IndexBuilder * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime * @param \Magento\Catalog\Model\ProductFactory $productFactory * @param int $batchCount + * @param \Magento\CatalogRule\Model\Indexer\ProductPriceCalculator|null $productPriceCalculator + * @param \Magento\CatalogRule\Model\Indexer\ReindexRuleProduct|null $reindexRuleProduct + * @param \Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite|null $reindexRuleGroupWebsite + * @param \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder|null $ruleProductsSelectBuilder + * @param \Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice|null $reindexRuleProductPrice + * @param \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor|null $pricesPersistor + * @param \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher|null $activeTableSwitcher * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -119,7 +163,14 @@ public function __construct( \Magento\Framework\Stdlib\DateTime $dateFormat, \Magento\Framework\Stdlib\DateTime\DateTime $dateTime, \Magento\Catalog\Model\ProductFactory $productFactory, - $batchCount = 1000 + $batchCount = 1000, + \Magento\CatalogRule\Model\Indexer\ProductPriceCalculator $productPriceCalculator = null, + \Magento\CatalogRule\Model\Indexer\ReindexRuleProduct $reindexRuleProduct = null, + \Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite $reindexRuleGroupWebsite = null, + \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder $ruleProductsSelectBuilder = null, + \Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice $reindexRuleProductPrice = null, + \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor $pricesPersistor = null, + \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher = null ) { $this->resource = $resource; $this->connection = $resource->getConnection(); @@ -132,6 +183,28 @@ public function __construct( $this->dateTime = $dateTime; $this->productFactory = $productFactory; $this->batchCount = $batchCount; + + $this->productPriceCalculator = $productPriceCalculator ?: ObjectManager::getInstance()->get( + \Magento\CatalogRule\Model\Indexer\ProductPriceCalculator::class + ); + $this->reindexRuleProduct = $reindexRuleProduct ?: ObjectManager::getInstance()->get( + \Magento\CatalogRule\Model\Indexer\ReindexRuleProduct::class + ); + $this->reindexRuleGroupWebsite = $reindexRuleGroupWebsite ?: ObjectManager::getInstance()->get( + \Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite::class + ); + $this->ruleProductsSelectBuilder = $ruleProductsSelectBuilder ?: ObjectManager::getInstance()->get( + \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder::class + ); + $this->reindexRuleProductPrice = $reindexRuleProductPrice ?: ObjectManager::getInstance()->get( + \Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice::class + ); + $this->pricesPersistor = $pricesPersistor ?: ObjectManager::getInstance()->get( + \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor::class + ); + $this->activeTableSwitcher = $activeTableSwitcher ?: ObjectManager::getInstance()->get( + \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class + ); } /** @@ -209,10 +282,28 @@ public function reindexFull() */ protected function doReindexFull() { + $this->connection->truncateTable( + $this->getTable($this->activeTableSwitcher->getAdditionalTableName('catalogrule_product')) + ); + $this->connection->truncateTable( + $this->getTable($this->activeTableSwitcher->getAdditionalTableName('catalogrule_product_price')) + ); + foreach ($this->getAllRules() as $rule) { - $this->updateRuleProductData($rule); + $this->reindexRuleProduct->execute($rule, $this->batchCount, true); } - $this->deleteOldData()->applyAllRules(); + + $this->reindexRuleProductPrice->execute($this->batchCount, null, true); + $this->reindexRuleGroupWebsite->execute(true); + + $this->activeTableSwitcher->switchTable( + $this->connection, + [ + $this->getTable('catalogrule_product'), + $this->getTable('catalogrule_product_price'), + $this->getTable('catalogrule_group_website') + ] + ); } /** @@ -308,7 +399,8 @@ protected function applyRule(Rule $rule, $product) throw $e; } - $this->applyAllRules($product); + $this->reindexRuleProductPrice->execute($this->batchCount, $product); + $this->reindexRuleGroupWebsite->execute(); return $this; } @@ -325,8 +417,8 @@ protected function getTable($tableName) /** * @param Rule $rule * @return $this - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) + * @deprecated + * @see \Magento\CatalogRule\Model\Indexer\ReindexRuleProduct::execute */ protected function updateRuleProductData(Rule $rule) { @@ -343,63 +435,7 @@ protected function updateRuleProductData(Rule $rule) ); } - if (!$rule->getIsActive()) { - return $this; - } - - $websiteIds = $rule->getWebsiteIds(); - if (!is_array($websiteIds)) { - $websiteIds = explode(',', $websiteIds); - } - if (empty($websiteIds)) { - return $this; - } - - \Magento\Framework\Profiler::start('__MATCH_PRODUCTS__'); - $productIds = $rule->getMatchingProductIds(); - \Magento\Framework\Profiler::stop('__MATCH_PRODUCTS__'); - - $customerGroupIds = $rule->getCustomerGroupIds(); - $fromTime = strtotime($rule->getFromDate()); - $toTime = strtotime($rule->getToDate()); - $toTime = $toTime ? $toTime + self::SECONDS_IN_DAY - 1 : 0; - $sortOrder = (int)$rule->getSortOrder(); - $actionOperator = $rule->getSimpleAction(); - $actionAmount = $rule->getDiscountAmount(); - $actionStop = $rule->getStopRulesProcessing(); - - $rows = []; - - foreach ($productIds as $productId => $validationByWebsite) { - foreach ($websiteIds as $websiteId) { - if (empty($validationByWebsite[$websiteId])) { - continue; - } - foreach ($customerGroupIds as $customerGroupId) { - $rows[] = [ - 'rule_id' => $ruleId, - 'from_time' => $fromTime, - 'to_time' => $toTime, - 'website_id' => $websiteId, - 'customer_group_id' => $customerGroupId, - 'product_id' => $productId, - 'action_operator' => $actionOperator, - 'action_amount' => $actionAmount, - 'action_stop' => $actionStop, - 'sort_order' => $sortOrder, - ]; - - if (count($rows) == $this->batchCount) { - $this->connection->insertMultiple($this->getTable('catalogrule_product'), $rows); - $rows = []; - } - } - } - } - if (!empty($rows)) { - $this->connection->insertMultiple($this->getTable('catalogrule_product'), $rows); - } - + $this->reindexRuleProduct->execute($rule, $this->batchCount); return $this; } @@ -407,123 +443,27 @@ protected function updateRuleProductData(Rule $rule) * @param Product|null $product * @throws \Exception * @return $this - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @deprecated + * @see \Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice::execute + * @see \Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite::execute */ protected function applyAllRules(Product $product = null) { - $fromDate = mktime(0, 0, 0, date('m'), date('d') - 1); - $toDate = mktime(0, 0, 0, date('m'), date('d') + 1); - - /** - * Update products rules prices per each website separately - * because of max join limit in mysql - */ - foreach ($this->storeManager->getWebsites() as $website) { - $productsStmt = $this->getRuleProductsStmt($website->getId(), $product); - - $dayPrices = []; - $stopFlags = []; - $prevKey = null; - - while ($ruleData = $productsStmt->fetch()) { - $ruleProductId = $ruleData['product_id']; - $productKey = $ruleProductId . - '_' . - $ruleData['website_id'] . - '_' . - $ruleData['customer_group_id']; - - if ($prevKey && $prevKey != $productKey) { - $stopFlags = []; - if (count($dayPrices) > $this->batchCount) { - $this->saveRuleProductPrices($dayPrices); - $dayPrices = []; - } - } - - $ruleData['from_time'] = $this->roundTime($ruleData['from_time']); - $ruleData['to_time'] = $this->roundTime($ruleData['to_time']); - /** - * Build prices for each day - */ - for ($time = $fromDate; $time <= $toDate; $time += self::SECONDS_IN_DAY) { - if (($ruleData['from_time'] == 0 || - $time >= $ruleData['from_time']) && ($ruleData['to_time'] == 0 || - $time <= $ruleData['to_time']) - ) { - $priceKey = $time . '_' . $productKey; - - if (isset($stopFlags[$priceKey])) { - continue; - } - - if (!isset($dayPrices[$priceKey])) { - $dayPrices[$priceKey] = [ - 'rule_date' => $time, - 'website_id' => $ruleData['website_id'], - 'customer_group_id' => $ruleData['customer_group_id'], - 'product_id' => $ruleProductId, - 'rule_price' => $this->calcRuleProductPrice($ruleData), - 'latest_start_date' => $ruleData['from_time'], - 'earliest_end_date' => $ruleData['to_time'], - ]; - } else { - $dayPrices[$priceKey]['rule_price'] = $this->calcRuleProductPrice( - $ruleData, - $dayPrices[$priceKey] - ); - $dayPrices[$priceKey]['latest_start_date'] = max( - $dayPrices[$priceKey]['latest_start_date'], - $ruleData['from_time'] - ); - $dayPrices[$priceKey]['earliest_end_date'] = min( - $dayPrices[$priceKey]['earliest_end_date'], - $ruleData['to_time'] - ); - } - - if ($ruleData['action_stop']) { - $stopFlags[$priceKey] = true; - } - } - } - - $prevKey = $productKey; - } - $this->saveRuleProductPrices($dayPrices); - } - - return $this->updateCatalogRuleGroupWebsiteData(); + $this->reindexRuleProductPrice->execute($this->batchCount, $product); + $this->reindexRuleGroupWebsite->execute(); + return $this; } /** * Update CatalogRuleGroupWebsite data * * @return $this + * @deprecated + * @see \Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite::execute */ protected function updateCatalogRuleGroupWebsiteData() { - $this->connection->delete($this->getTable('catalogrule_group_website'), []); - - $timestamp = $this->dateTime->gmtTimestamp(); - - $select = $this->connection->select()->distinct( - true - )->from( - $this->getTable('catalogrule_product'), - $this->_catalogRuleGroupWebsiteColumnsList - )->where( - "{$timestamp} >= from_time AND (({$timestamp} <= to_time AND to_time > 0) OR to_time = 0)" - ); - $query = $select->insertFromSelect( - $this->getTable('catalogrule_group_website'), - $this->_catalogRuleGroupWebsiteColumnsList - ); - - $this->connection->query($query); - + $this->reindexRuleGroupWebsite->execute(); return $this; } @@ -542,33 +482,12 @@ protected function deleteOldData() * @param array $ruleData * @param null $productData * @return float + * @deprecated + * @see \Magento\CatalogRule\Model\Indexer\ProductPriceCalculator::calculate */ protected function calcRuleProductPrice($ruleData, $productData = null) { - if ($productData !== null && isset($productData['rule_price'])) { - $productPrice = $productData['rule_price']; - } else { - $productPrice = $ruleData['default_price']; - } - - switch ($ruleData['action_operator']) { - case 'to_fixed': - $productPrice = min($ruleData['action_amount'], $productPrice); - break; - case 'to_percent': - $productPrice = $productPrice * $ruleData['action_amount'] / 100; - break; - case 'by_fixed': - $productPrice = max(0, $productPrice - $ruleData['action_amount']); - break; - case 'by_percent': - $productPrice = $productPrice * (1 - $ruleData['action_amount'] / 100); - break; - default: - $productPrice = 0; - } - - return $this->priceCurrency->round($productPrice); + return $this->productPriceCalculator->calculate($ruleData, $productData); } /** @@ -576,107 +495,24 @@ protected function calcRuleProductPrice($ruleData, $productData = null) * @param Product|null $product * @return \Zend_Db_Statement_Interface * @throws \Magento\Framework\Exception\LocalizedException + * @deprecated + * @see \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder::build */ protected function getRuleProductsStmt($websiteId, Product $product = null) { - /** - * Sort order is important - * It used for check stop price rule condition. - * website_id customer_group_id product_id sort_order - * 1 1 1 0 - * 1 1 1 1 - * 1 1 1 2 - * if row with sort order 1 will have stop flag we should exclude - * all next rows for same product id from price calculation - */ - $select = $this->connection->select()->from( - ['rp' => $this->getTable('catalogrule_product')] - )->order( - ['rp.website_id', 'rp.customer_group_id', 'rp.product_id', 'rp.sort_order', 'rp.rule_id'] - ); - - if ($product && $product->getEntityId()) { - $select->where('rp.product_id=?', $product->getEntityId()); - } - - /** - * Join default price and websites prices to result - */ - $priceAttr = $this->eavConfig->getAttribute(Product::ENTITY, 'price'); - $priceTable = $priceAttr->getBackend()->getTable(); - $attributeId = $priceAttr->getId(); - - $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); - $select->join( - ['e' => $this->getTable('catalog_product_entity')], - sprintf('e.entity_id = rp.product_id'), - [] - ); - $joinCondition = '%1$s.' . $linkField . '=e.' . $linkField . ' AND (%1$s.attribute_id=' - . $attributeId - . ') and %1$s.store_id=%2$s'; - - $select->join( - ['pp_default' => $priceTable], - sprintf($joinCondition, 'pp_default', \Magento\Store\Model\Store::DEFAULT_STORE_ID), - [] - ); - - $website = $this->storeManager->getWebsite($websiteId); - $defaultGroup = $website->getDefaultGroup(); - if ($defaultGroup instanceof \Magento\Store\Model\Group) { - $storeId = $defaultGroup->getDefaultStoreId(); - } else { - $storeId = \Magento\Store\Model\Store::DEFAULT_STORE_ID; - } - - $select->joinInner( - ['product_website' => $this->getTable('catalog_product_website')], - 'product_website.product_id=rp.product_id ' - . 'AND product_website.website_id = rp.website_id ' - . 'AND product_website.website_id=' - . $websiteId, - [] - ); - - $tableAlias = 'pp' . $websiteId; - $select->joinLeft( - [$tableAlias => $priceTable], - sprintf($joinCondition, $tableAlias, $storeId), - [] - ); - $select->columns([ - 'default_price' =>$this->connection->getIfNullSql($tableAlias . '.value', 'pp_default.value'), - ]); - - return $this->connection->query($select); + return $this->ruleProductsSelectBuilder->build($websiteId, $product); } /** * @param array $arrData * @return $this * @throws \Exception + * @deprecated + * @see \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor::execute */ protected function saveRuleProductPrices($arrData) { - if (empty($arrData)) { - return $this; - } - - $productIds = []; - - try { - foreach ($arrData as $key => $data) { - $productIds['product_id'] = $data['product_id']; - $arrData[$key]['rule_date'] = $this->dateFormat->formatDate($data['rule_date'], false); - $arrData[$key]['latest_start_date'] = $this->dateFormat->formatDate($data['latest_start_date'], false); - $arrData[$key]['earliest_end_date'] = $this->dateFormat->formatDate($data['earliest_end_date'], false); - } - $this->connection->insertOnDuplicate($this->getTable('catalogrule_product_price'), $arrData); - } catch (\Exception $e) { - throw $e; - } - + $this->pricesPersistor->execute($arrData); return $this; } @@ -687,8 +523,7 @@ protected function saveRuleProductPrices($arrData) */ protected function getActiveRules() { - return $this->ruleCollectionFactory->create() - ->addFieldToFilter('is_active', 1); + return $this->ruleCollectionFactory->create()->addFieldToFilter('is_active', 1); } /** @@ -721,29 +556,4 @@ protected function critical($e) { $this->logger->critical($e); } - - /** - * @param int $timeStamp - * @return int - */ - private function roundTime($timeStamp) - { - if (is_numeric($timeStamp) && $timeStamp != 0) { - $timeStamp = $this->dateTime->timestamp($this->dateTime->date('Y-m-d 00:00:00', $timeStamp)); - } - - return $timeStamp; - } - - /** - * @return MetadataPool - */ - private function getMetadataPool() - { - if (null === $this->metadataPool) { - $this->metadataPool = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\EntityManager\MetadataPool::class); - } - return $this->metadataPool; - } } diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ProductPriceCalculator.php b/app/code/Magento/CatalogRule/Model/Indexer/ProductPriceCalculator.php new file mode 100644 index 0000000000000..9b6e6ad285117 --- /dev/null +++ b/app/code/Magento/CatalogRule/Model/Indexer/ProductPriceCalculator.php @@ -0,0 +1,57 @@ +priceCurrency = $priceCurrency; + } + + /** + * @param array $ruleData + * @param null $productData + * @return float + */ + public function calculate($ruleData, $productData = null) + { + if ($productData !== null && isset($productData['rule_price'])) { + $productPrice = $productData['rule_price']; + } else { + $productPrice = $ruleData['default_price']; + } + + switch ($ruleData['action_operator']) { + case 'to_fixed': + $productPrice = min($ruleData['action_amount'], $productPrice); + break; + case 'to_percent': + $productPrice = $productPrice * $ruleData['action_amount'] / 100; + break; + case 'by_fixed': + $productPrice = max(0, $productPrice - $ruleData['action_amount']); + break; + case 'by_percent': + $productPrice = $productPrice * (1 - $ruleData['action_amount'] / 100); + break; + default: + $productPrice = 0; + } + + return $this->priceCurrency->round($productPrice); + } +} diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleGroupWebsite.php b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleGroupWebsite.php new file mode 100644 index 0000000000000..3fd1522bd9f6a --- /dev/null +++ b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleGroupWebsite.php @@ -0,0 +1,79 @@ +dateTime = $dateTime; + $this->resource = $resource; + $this->activeTableSwitcher = $activeTableSwitcher; + } + + /** + * @param bool $useAdditionalTable + * @return bool + */ + public function execute($useAdditionalTable = false) + { + $connection = $this->resource->getConnection(); + $timestamp = $this->dateTime->gmtTimestamp(); + + $indexTable = $this->resource->getTableName('catalogrule_group_website'); + $ruleProductTable = $this->resource->getTableName('catalogrule_product'); + if ($useAdditionalTable) { + $indexTable = $this->resource->getTableName( + $this->activeTableSwitcher->getAdditionalTableName('catalogrule_group_website') + ); + $ruleProductTable = $this->resource->getTableName( + $this->activeTableSwitcher->getAdditionalTableName('catalogrule_product') + ); + } + + $connection->delete($indexTable); + $select = $connection->select()->distinct( + true + )->from( + $ruleProductTable, + $this->catalogRuleGroupWebsiteColumnsList + )->where( + "{$timestamp} >= from_time AND (({$timestamp} <= to_time AND to_time > 0) OR to_time = 0)" + ); + $query = $select->insertFromSelect($indexTable, $this->catalogRuleGroupWebsiteColumnsList); + $connection->query($query); + return true; + } +} diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php new file mode 100644 index 0000000000000..50f10b0c60ef3 --- /dev/null +++ b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php @@ -0,0 +1,110 @@ +resource = $resource; + $this->activeTableSwitcher = $activeTableSwitcher; + } + + /** + * @param \Magento\CatalogRule\Model\Rule $rule + * @param int $batchCount + * @param bool $useAdditionalTable + * @return bool + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + public function execute( + \Magento\CatalogRule\Model\Rule $rule, + $batchCount, + $useAdditionalTable = false + ) { + if (!$rule->getIsActive() || empty($rule->getWebsiteIds())) { + return false; + } + + $connection = $this->resource->getConnection(); + $websiteIds = $rule->getWebsiteIds(); + if (!is_array($websiteIds)) { + $websiteIds = explode(',', $websiteIds); + } + + \Magento\Framework\Profiler::start('__MATCH_PRODUCTS__'); + $productIds = $rule->getMatchingProductIds(); + \Magento\Framework\Profiler::stop('__MATCH_PRODUCTS__'); + + $indexTable = $this->resource->getTableName('catalogrule_product'); + if ($useAdditionalTable) { + $indexTable = $this->resource->getTableName( + $this->activeTableSwitcher->getAdditionalTableName('catalogrule_product') + ); + } + + $ruleId = $rule->getId(); + $customerGroupIds = $rule->getCustomerGroupIds(); + $fromTime = strtotime($rule->getFromDate()); + $toTime = strtotime($rule->getToDate()); + $toTime = $toTime ? $toTime + \Magento\CatalogRule\Model\Indexer\IndexBuilder::SECONDS_IN_DAY - 1 : 0; + $sortOrder = (int)$rule->getSortOrder(); + $actionOperator = $rule->getSimpleAction(); + $actionAmount = $rule->getDiscountAmount(); + $actionStop = $rule->getStopRulesProcessing(); + + $rows = []; + + foreach ($productIds as $productId => $validationByWebsite) { + foreach ($websiteIds as $websiteId) { + if (empty($validationByWebsite[$websiteId])) { + continue; + } + foreach ($customerGroupIds as $customerGroupId) { + $rows[] = [ + 'rule_id' => $ruleId, + 'from_time' => $fromTime, + 'to_time' => $toTime, + 'website_id' => $websiteId, + 'customer_group_id' => $customerGroupId, + 'product_id' => $productId, + 'action_operator' => $actionOperator, + 'action_amount' => $actionAmount, + 'action_stop' => $actionStop, + 'sort_order' => $sortOrder, + ]; + + if (count($rows) == $batchCount) { + $connection->insertMultiple($indexTable, $rows); + $rows = []; + } + } + } + } + if (!empty($rows)) { + $connection->insertMultiple($indexTable, $rows); + } + return true; + } +} diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php new file mode 100644 index 0000000000000..32aba848338f5 --- /dev/null +++ b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php @@ -0,0 +1,163 @@ +storeManager = $storeManager; + $this->ruleProductsSelectBuilder = $ruleProductsSelectBuilder; + $this->productPriceCalculator = $productPriceCalculator; + $this->dateTime = $dateTime; + $this->pricesPersistor = $pricesPersistor; + } + + /** + * @param int $batchCount + * @param \Magento\Catalog\Model\Product|null $product + * @param bool $useAdditionalTable + * @return bool + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + public function execute( + $batchCount, + \Magento\Catalog\Model\Product $product = null, + $useAdditionalTable = false + ) { + $fromDate = mktime(0, 0, 0, date('m'), date('d') - 1); + $toDate = mktime(0, 0, 0, date('m'), date('d') + 1); + + /** + * Update products rules prices per each website separately + * because of max join limit in mysql + */ + foreach ($this->storeManager->getWebsites() as $website) { + $productsStmt = $this->ruleProductsSelectBuilder->build($website->getId(), $product, $useAdditionalTable); + $dayPrices = []; + $stopFlags = []; + $prevKey = null; + + while ($ruleData = $productsStmt->fetch()) { + $ruleProductId = $ruleData['product_id']; + $productKey = $ruleProductId . + '_' . + $ruleData['website_id'] . + '_' . + $ruleData['customer_group_id']; + + if ($prevKey && $prevKey != $productKey) { + $stopFlags = []; + if (count($dayPrices) > $batchCount) { + $this->pricesPersistor->execute($dayPrices, $useAdditionalTable); + $dayPrices = []; + } + } + + $ruleData['from_time'] = $this->roundTime($ruleData['from_time']); + $ruleData['to_time'] = $this->roundTime($ruleData['to_time']); + /** + * Build prices for each day + */ + for ($time = $fromDate; $time <= $toDate; $time += IndexBuilder::SECONDS_IN_DAY) { + if (($ruleData['from_time'] == 0 || + $time >= $ruleData['from_time']) && ($ruleData['to_time'] == 0 || + $time <= $ruleData['to_time']) + ) { + $priceKey = $time . '_' . $productKey; + + if (isset($stopFlags[$priceKey])) { + continue; + } + + if (!isset($dayPrices[$priceKey])) { + $dayPrices[$priceKey] = [ + 'rule_date' => $time, + 'website_id' => $ruleData['website_id'], + 'customer_group_id' => $ruleData['customer_group_id'], + 'product_id' => $ruleProductId, + 'rule_price' => $this->productPriceCalculator->calculate($ruleData), + 'latest_start_date' => $ruleData['from_time'], + 'earliest_end_date' => $ruleData['to_time'], + ]; + } else { + $dayPrices[$priceKey]['rule_price'] = $this->productPriceCalculator->calculate( + $ruleData, + $dayPrices[$priceKey] + ); + $dayPrices[$priceKey]['latest_start_date'] = max( + $dayPrices[$priceKey]['latest_start_date'], + $ruleData['from_time'] + ); + $dayPrices[$priceKey]['earliest_end_date'] = min( + $dayPrices[$priceKey]['earliest_end_date'], + $ruleData['to_time'] + ); + } + + if ($ruleData['action_stop']) { + $stopFlags[$priceKey] = true; + } + } + } + + $prevKey = $productKey; + } + $this->pricesPersistor->execute($dayPrices, $useAdditionalTable); + } + return true; + } + + /** + * @param int $timeStamp + * @return int + */ + private function roundTime($timeStamp) + { + if (is_numeric($timeStamp) && $timeStamp != 0) { + $timeStamp = $this->dateTime->timestamp($this->dateTime->date('Y-m-d 00:00:00', $timeStamp)); + } + return $timeStamp; + } +} diff --git a/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php new file mode 100644 index 0000000000000..c920a1dd20cf2 --- /dev/null +++ b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php @@ -0,0 +1,76 @@ +dateFormat = $dateFormat; + $this->resource = $resource; + $this->activeTableSwitcher = $activeTableSwitcher; + } + + /** + * @param array $arrData + * @param bool $useAdditionalTable + * @return bool + * @throws \Exception + */ + public function execute(array $arrData, $useAdditionalTable = false) + { + if (empty($arrData)) { + return false; + } + + $connection = $this->resource->getConnection(); + $indexTable = $this->resource->getTableName('catalogrule_product_price'); + if ($useAdditionalTable) { + $indexTable = $this->resource->getTableName( + $this->activeTableSwitcher->getAdditionalTableName('catalogrule_product_price') + ); + } + + $productIds = []; + + try { + foreach ($arrData as $key => $data) { + $productIds['product_id'] = $data['product_id']; + $arrData[$key]['rule_date'] = $this->dateFormat->formatDate($data['rule_date'], false); + $arrData[$key]['latest_start_date'] = $this->dateFormat->formatDate($data['latest_start_date'], false); + $arrData[$key]['earliest_end_date'] = $this->dateFormat->formatDate($data['earliest_end_date'], false); + } + $connection->insertOnDuplicate($indexTable, $arrData); + } catch (\Exception $e) { + throw $e; + } + return true; + } +} diff --git a/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php new file mode 100644 index 0000000000000..41e620d5721ee --- /dev/null +++ b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php @@ -0,0 +1,150 @@ +eavConfig = $eavConfig; + $this->storeManager = $storeManager; + $this->metadataPool = $metadataPool; + $this->resource = $resource; + $this->activeTableSwitcher = $activeTableSwitcher; + } + + /** + * @param int $websiteId + * @param \Magento\Catalog\Model\Product|null $product + * @param bool $useAdditionalTable + * @return \Zend_Db_Statement_Interface + */ + public function build( + $websiteId, + \Magento\Catalog\Model\Product $product = null, + $useAdditionalTable = false + ) { + $connection = $this->resource->getConnection(); + $indexTable = $this->resource->getTableName('catalogrule_product'); + if ($useAdditionalTable) { + $indexTable = $this->resource->getTableName( + $this->activeTableSwitcher->getAdditionalTableName('catalogrule_product') + ); + } + + /** + * Sort order is important + * It used for check stop price rule condition. + * website_id customer_group_id product_id sort_order + * 1 1 1 0 + * 1 1 1 1 + * 1 1 1 2 + * if row with sort order 1 will have stop flag we should exclude + * all next rows for same product id from price calculation + */ + $select = $connection->select()->from( + ['rp' => $indexTable] + )->order( + ['rp.website_id', 'rp.customer_group_id', 'rp.product_id', 'rp.sort_order', 'rp.rule_id'] + ); + + if ($product && $product->getEntityId()) { + $select->where('rp.product_id=?', $product->getEntityId()); + } + + /** + * Join default price and websites prices to result + */ + $priceAttr = $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, 'price'); + $priceTable = $priceAttr->getBackend()->getTable(); + $attributeId = $priceAttr->getId(); + + $linkField = $this->metadataPool + ->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class) + ->getLinkField(); + $select->join( + ['e' => $this->resource->getTableName('catalog_product_entity')], + sprintf('e.entity_id = rp.product_id'), + [] + ); + $joinCondition = '%1$s.' . $linkField . '=e.' . $linkField . ' AND (%1$s.attribute_id=' + . $attributeId + . ') and %1$s.store_id=%2$s'; + + $select->join( + ['pp_default' => $priceTable], + sprintf($joinCondition, 'pp_default', \Magento\Store\Model\Store::DEFAULT_STORE_ID), + [] + ); + + $website = $this->storeManager->getWebsite($websiteId); + $defaultGroup = $website->getDefaultGroup(); + if ($defaultGroup instanceof \Magento\Store\Model\Group) { + $storeId = $defaultGroup->getDefaultStoreId(); + } else { + $storeId = \Magento\Store\Model\Store::DEFAULT_STORE_ID; + } + + $select->joinInner( + ['product_website' => $this->resource->getTableName('catalog_product_website')], + 'product_website.product_id=rp.product_id ' + . 'AND product_website.website_id = rp.website_id ' + . 'AND product_website.website_id=' + . $websiteId, + [] + ); + + $tableAlias = 'pp' . $websiteId; + $select->joinLeft( + [$tableAlias => $priceTable], + sprintf($joinCondition, $tableAlias, $storeId), + [] + ); + $select->columns([ + 'default_price' =>$connection->getIfNullSql($tableAlias . '.value', 'pp_default.value'), + ]); + + return $connection->query($select); + } +} diff --git a/app/code/Magento/CatalogRule/Model/Rule.php b/app/code/Magento/CatalogRule/Model/Rule.php index 565a2fc6f617d..c3cf0a2aed49c 100644 --- a/app/code/Magento/CatalogRule/Model/Rule.php +++ b/app/code/Magento/CatalogRule/Model/Rule.php @@ -521,7 +521,7 @@ public function afterSave() if ($this->isObjectNew()) { $this->getMatchingProductIds(); if (!empty($this->_productIds) && is_array($this->_productIds)) { - $this->_ruleProductProcessor->reindexList($this->_productIds); + $this->_getResource()->addCommitCallback([$this, 'reindex']); } } else { $this->_ruleProductProcessor->getIndexer()->invalidate(); @@ -529,6 +529,16 @@ public function afterSave() return parent::afterSave(); } + /** + * Init indexing process after rule save + * + * @return void + */ + public function reindex() + { + $this->_ruleProductProcessor->reindexList($this->_productIds); + } + /** * {@inheritdoc} * diff --git a/app/code/Magento/CatalogRule/Setup/UpgradeSchema.php b/app/code/Magento/CatalogRule/Setup/UpgradeSchema.php index fa64c93b3313f..5f3271b2f1a90 100644 --- a/app/code/Magento/CatalogRule/Setup/UpgradeSchema.php +++ b/app/code/Magento/CatalogRule/Setup/UpgradeSchema.php @@ -40,6 +40,31 @@ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $con } } + if (version_compare($context->getVersion(), '2.2.0', '<')) { + $connection = $setup->getConnection(); + $connection->dropForeignKey( + $setup->getTable('catalogrule_group_website'), + $setup->getFkName( + 'catalogrule_group_website', + 'customer_group_id', + 'customer_group', + 'customer_group_id' + ) + ); + $connection->dropForeignKey( + $setup->getTable('catalogrule_group_website'), + $setup->getFkName('catalogrule_group_website', 'rule_id', 'catalogrule', 'rule_id') + ); + $connection->dropForeignKey( + $setup->getTable('catalogrule_group_website'), + $setup->getFkName('catalogrule_group_website', 'website_id', 'store_website', 'website_id') + ); + + $this->addReplicaTable($setup, 'catalogrule_product', 'catalogrule_product_replica'); + $this->addReplicaTable($setup, 'catalogrule_product_price', 'catalogrule_product_price_replica'); + $this->addReplicaTable($setup, 'catalogrule_group_website', 'catalogrule_group_website_replica'); + } + $setup->endSetup(); } @@ -69,4 +94,22 @@ private function removeSubProductDiscounts(SchemaSetupInterface $setup) } } } + + /** + * Add the replica table for existing one. + * + * @param SchemaSetupInterface $setup + * @param string $existingTable + * @param string $replicaTable + * @return void + */ + private function addReplicaTable(SchemaSetupInterface $setup, $existingTable, $replicaTable) + { + $sql = sprintf( + 'CREATE TABLE IF NOT EXISTS %s LIKE %s', + $setup->getTable($replicaTable), + $setup->getTable($existingTable) + ); + $setup->getConnection()->query($sql); + } } diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php index 7cae9db9f7163..4f967b02b99ca 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php @@ -112,6 +112,16 @@ class IndexBuilderTest extends \PHPUnit_Framework_TestCase */ protected $backend; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $reindexRuleProductPrice; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $reindexRuleGroupWebsite; + /** * Set up test * @@ -166,34 +176,26 @@ protected function setUp() $this->eavConfig = $this->getMock(\Magento\Eav\Model\Config::class, ['getAttribute'], [], '', false); $this->product = $this->getMock(\Magento\Catalog\Model\Product::class, [], [], '', false); $this->productFactory = $this->getMock(\Magento\Catalog\Model\ProductFactory::class, ['create'], [], '', false); - $this->connection->expects($this->any())->method('select')->will($this->returnValue($this->select)); $this->connection->expects($this->any())->method('query')->will($this->returnValue($this->db)); - $this->select->expects($this->any())->method('distinct')->will($this->returnSelf()); $this->select->expects($this->any())->method('where')->will($this->returnSelf()); $this->select->expects($this->any())->method('from')->will($this->returnSelf()); $this->select->expects($this->any())->method('order')->will($this->returnSelf()); - $this->resource->expects($this->any())->method('getConnection')->will($this->returnValue($this->connection)); $this->resource->expects($this->any())->method('getTableName')->will($this->returnArgument(0)); - $this->storeManager->expects($this->any())->method('getWebsites')->will($this->returnValue([$this->website])); $this->storeManager->expects($this->any())->method('getWebsite')->will($this->returnValue($this->website)); - $this->rules->expects($this->any())->method('getId')->will($this->returnValue(1)); $this->rules->expects($this->any())->method('getWebsiteIds')->will($this->returnValue([1])); $this->rules->expects($this->any())->method('getCustomerGroupIds')->will($this->returnValue([1])); - $this->ruleCollectionFactory->expects($this->any())->method('create')->will($this->returnSelf()); $this->ruleCollectionFactory->expects($this->any())->method('addFieldToFilter')->will( $this->returnValue([$this->rules]) ); - $this->product->expects($this->any())->method('load')->will($this->returnSelf()); $this->product->expects($this->any())->method('getId')->will($this->returnValue(1)); $this->product->expects($this->any())->method('getWebsiteIds')->will($this->returnValue([1])); - $this->rules->expects($this->any())->method('validate')->with($this->product)->willReturn(true); $this->attribute->expects($this->any())->method('getBackend')->will($this->returnValue($this->backend)); $this->productFactory->expects($this->any())->method('create')->will($this->returnValue($this->product)); @@ -209,9 +211,18 @@ protected function setUp() $this->dateTime, $this->productFactory ); - + $this->reindexRuleProductPrice = + $this->getMockBuilder(\Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice::class) + ->disableOriginalConstructor() + ->getMock(); + $this->reindexRuleGroupWebsite = + $this->getMockBuilder(\Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite::class) + ->disableOriginalConstructor() + ->getMock(); $this->setProperties($this->indexBuilder, [ - 'metadataPool' => $this->metadataPool + 'metadataPool' => $this->metadataPool, + 'reindexRuleProductPrice' => $this->reindexRuleProductPrice, + 'reindexRuleGroupWebsite' => $this->reindexRuleGroupWebsite ]); } @@ -253,12 +264,9 @@ public function testUpdateCatalogRuleGroupWebsiteData() $priceAttrMock->expects($this->any()) ->method('getBackend') ->will($this->returnValue($backendModelMock)); - $this->eavConfig->expects($this->at(0)) - ->method('getAttribute') - ->with(\Magento\Catalog\Model\Product::ENTITY, 'price') - ->will($this->returnValue($this->attribute)); - $this->select->expects($this->once())->method('insertFromSelect')->with('catalogrule_group_website'); + $this->reindexRuleProductPrice->expects($this->once())->method('execute')->willReturn(true); + $this->reindexRuleGroupWebsite->expects($this->once())->method('execute')->willReturn(true); $this->indexBuilder->reindexByIds([1]); } diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundleWithCatalogPriceRuleCalculatorTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundleWithCatalogPriceRuleCalculatorTest.php index 624d57f7b38fb..3c756041eaccd 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundleWithCatalogPriceRuleCalculatorTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundleWithCatalogPriceRuleCalculatorTest.php @@ -7,7 +7,9 @@ namespace Magento\Bundle\Model\Product; /** - * @magentoDataFixture Magento/Bundle/_files/PriceCalculator/dynamic_bundle_product_with_catalog_rule.php + * @codingStandardsIgnoreStart + * @magentoDataFixtureBeforeTransaction Magento/Bundle/_files/PriceCalculator/dynamic_bundle_product_with_catalog_rule.php + * @codingStandardsIgnoreEnd * @magentoAppArea frontend */ class DynamicBundleWithCatalogPriceRuleCalculatorTest extends BundlePriceAbstract diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/dynamic_bundle_product_with_catalog_rule_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/dynamic_bundle_product_with_catalog_rule_rollback.php index 5f1ad8cdf4be5..bd91360bf098b 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/dynamic_bundle_product_with_catalog_rule_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/dynamic_bundle_product_with_catalog_rule_rollback.php @@ -5,3 +5,4 @@ */ require __DIR__ . '/dynamic_bundle_product_rollback.php'; +require __DIR__ . '/../../../CatalogRule/_files/catalog_rule_10_off_not_logged_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/fixed_bundle_product_with_catalog_rule_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/fixed_bundle_product_with_catalog_rule_rollback.php index bf56e52b51c39..efae9845d61f0 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/fixed_bundle_product_with_catalog_rule_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/PriceCalculator/fixed_bundle_product_with_catalog_rule_rollback.php @@ -5,3 +5,4 @@ */ require __DIR__ . '/fixed_bundle_product_rollback.php'; +require __DIR__ . '/../../../CatalogRule/_files/catalog_rule_10_off_not_logged_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/BatchIndexTest.php b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/BatchIndexTest.php index 6c58308896323..0282a6bfbd5cf 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/BatchIndexTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/BatchIndexTest.php @@ -8,12 +8,6 @@ use Magento\TestFramework\Helper\Bootstrap; -/** - * @magentoAppIsolation enabled - * @magentoAppArea adminhtml - * @magentoDataFixture Magento/CatalogRule/_files/two_rules.php - * @magentoDataFixture Magento/Catalog/_files/product_simple.php - */ class BatchIndexTest extends \PHPUnit_Framework_TestCase { /** @@ -38,9 +32,34 @@ protected function setUp() $this->productRepository = Bootstrap::getObjectManager()->get(\Magento\Catalog\Model\ProductRepository::class); } + protected function tearDown() + { + /** @var \Magento\Framework\Registry $registry */ + $registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Framework\Registry::class); + + $registry->unregister('isSecureArea'); + $registry->register('isSecureArea', true); + + /** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $productCollection */ + $productCollection = Bootstrap::getObjectManager()->get( + \Magento\Catalog\Model\ResourceModel\Product\Collection::class + ); + $productCollection->delete(); + + $registry->unregister('isSecureArea'); + $registry->register('isSecureArea', false); + + parent::tearDown(); + } + /** * @magentoDbIsolation enabled * @dataProvider dataProvider + * @magentoAppIsolation enabled + * @magentoAppArea adminhtml + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/two_rules.php + * @magentoDataFixture Magento/Catalog/_files/product_simple.php */ public function testPriceForSmallBatch($batchCount, $price, $expectedPrice) { diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/IndexerBuilderTest.php b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/IndexerBuilderTest.php index 0a2fd69041a31..6b0f02d8d394f 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/IndexerBuilderTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/IndexerBuilderTest.php @@ -43,6 +43,27 @@ protected function setUp() $this->product = Bootstrap::getObjectManager()->get(\Magento\Catalog\Model\Product::class); } + protected function tearDown() + { + /** @var \Magento\Framework\Registry $registry */ + $registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Framework\Registry::class); + + $registry->unregister('isSecureArea'); + $registry->register('isSecureArea', true); + + /** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $productCollection */ + $productCollection = Bootstrap::getObjectManager()->get( + \Magento\Catalog\Model\ResourceModel\Product\Collection::class + ); + $productCollection->delete(); + + $registry->unregister('isSecureArea'); + $registry->register('isSecureArea', false); + + parent::tearDown(); + } + /** * @magentoDbIsolation enabled * @magentoAppIsolation enabled @@ -91,8 +112,8 @@ public function testReindexByIds() /** * @magentoDbIsolation enabled * @magentoAppIsolation enabled - * @magentoDataFixture Magento/CatalogRule/_files/attribute.php - * @magentoDataFixture Magento/CatalogRule/_files/rule_by_attribute.php + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/attribute.php + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/rule_by_attribute.php * @magentoDataFixture Magento/Catalog/_files/product_simple.php */ public function testReindexFull() diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/RuleProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/RuleProductTest.php index bfc0e28d28e9f..2ada01336aded 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/RuleProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/RuleProductTest.php @@ -32,7 +32,8 @@ protected function setUp() } /** - * @magentoDataFixture Magento/CatalogRule/_files/attribute.php + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/attribute.php + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/rule_by_attribute.php * @magentoDataFixture Magento/Catalog/_files/product_simple.php */ public function testReindexAfterRuleCreation() @@ -45,15 +46,9 @@ public function testReindexAfterRuleCreation() $product->setData('test_attribute', 'test_attribute_value')->save(); $this->assertFalse($this->resourceRule->getRulePrice(new \DateTime(), 1, 1, $product->getId())); - $this->saveRule(); // apply all rules $this->indexBuilder->reindexFull(); $this->assertEquals(9.8, $this->resourceRule->getRulePrice(new \DateTime(), 1, 1, $product->getId())); } - - protected function saveRule() - { - require 'Magento/CatalogRule/_files/rule_by_attribute.php'; - } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/attribute_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/attribute_rollback.php new file mode 100644 index 0000000000000..7c2973e7799db --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/attribute_rollback.php @@ -0,0 +1,20 @@ +create(\Magento\Catalog\Setup\CategorySetup::class); + +/** @var \Magento\Eav\Api\AttributeRepositoryInterface $eavRepository */ +$eavRepository = $objectManager->get(\Magento\Eav\Api\AttributeRepositoryInterface::class); + +try { + $attribute = $eavRepository->get($installer->getEntityTypeId('catalog_product'), 'test_attribute'); + $eavRepository->delete($attribute); +} catch (\Exception $ex) { + //Nothing to remove +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged_rollback.php new file mode 100644 index 0000000000000..e7985e8d6b149 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged_rollback.php @@ -0,0 +1,28 @@ +create(\Magento\CatalogRule\Model\ResourceModel\Rule::class); +$connection = $catalogRuleResource->getConnection(); + +//Retrieve rule id by name +$select = $connection->select(); +$select->from($catalogRuleResource->getMainTable(), 'rule_id'); +$select->where('name = ?', 'Test Catalog Rule'); +$ruleId = $connection->fetchOne($select); + +try { + /** @var \Magento\CatalogRule\Api\CatalogRuleRepositoryInterface $ruleRepository */ + $ruleRepository = $objectManager->create(\Magento\CatalogRule\Api\CatalogRuleRepositoryInterface::class); + $ruleRepository->deleteById($ruleId); +} catch (\Exception $ex) { + //Nothing to remove +} +/** @var \Magento\CatalogRule\Model\Indexer\IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(\Magento\CatalogRule\Model\Indexer\IndexBuilder::class); +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_by_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_by_attribute_rollback.php new file mode 100644 index 0000000000000..b1dd142b03de3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_by_attribute_rollback.php @@ -0,0 +1,27 @@ +create(\Magento\CatalogRule\Model\ResourceModel\Rule::class); + +//Retrieve rule id by name +$select = $catalogRuleResource->getConnection()->select(); +$select->from($catalogRuleResource->getMainTable(), 'rule_id'); +$select->where('name = ?', 'test_rule'); +$ruleId = $catalogRuleResource->getConnection()->fetchOne($select); + +try { + /** @var \Magento\CatalogRule\Api\CatalogRuleRepositoryInterface $ruleRepository */ + $ruleRepository = $objectManager->create(\Magento\CatalogRule\Api\CatalogRuleRepositoryInterface::class); + $ruleRepository->deleteById($ruleId); +} catch (\Exception $ex) { + //Nothing to remove +} +/** @var \Magento\CatalogRule\Model\Indexer\IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(\Magento\CatalogRule\Model\Indexer\IndexBuilder::class); +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/two_rules_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/two_rules_rollback.php new file mode 100644 index 0000000000000..3116a096457a9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/two_rules_rollback.php @@ -0,0 +1,30 @@ +create(\Magento\CatalogRule\Model\ResourceModel\Rule::class); + +/** @var \Magento\CatalogRule\Api\CatalogRuleRepositoryInterface $ruleRepository */ +$ruleRepository = $objectManager->create(\Magento\CatalogRule\Api\CatalogRuleRepositoryInterface::class); + +foreach (['test_rule_one', 'test_rule_two'] as $ruleName) { + //Retrieve rule id by name + $select = $catalogRuleResource->getConnection()->select(); + $select->from($catalogRuleResource->getMainTable(), 'rule_id'); + $select->where('name = ?', $ruleName); + $ruleId = $catalogRuleResource->getConnection()->fetchOne($select); + + try { + $ruleRepository->deleteById($ruleId); + } catch (\Exception $ex) { + //Nothing to remove + } +} + +/** @var \Magento\CatalogRule\Model\Indexer\IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(\Magento\CatalogRule\Model\Indexer\IndexBuilder::class); +$indexBuilder->reindexFull(); From eb0f7bec773327b275703c3d383b1aa6a2bbba5e Mon Sep 17 00:00:00 2001 From: RomanKis Date: Wed, 21 Jun 2017 16:53:13 +0300 Subject: [PATCH 27/39] MAGETWO-60533: Data passed to Magento\Framework\ObjectManager\Config\Compiled is not validated --- .../ObjectManager/Config/Compiled.php | 17 +-- .../Test/Unit/Config/CompiledTest.php | 118 ++++++++++++++++++ 2 files changed, 127 insertions(+), 8 deletions(-) diff --git a/lib/internal/Magento/Framework/ObjectManager/Config/Compiled.php b/lib/internal/Magento/Framework/ObjectManager/Config/Compiled.php index b011ba2e4b811..ef94b9efe665b 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Config/Compiled.php +++ b/lib/internal/Magento/Framework/ObjectManager/Config/Compiled.php @@ -30,15 +30,16 @@ class Compiled implements ConfigInterface private $preferences; /** - * Constructor - * * @param array $data */ public function __construct($data) { - $this->arguments = $data['arguments'] ?: []; - $this->virtualTypes = $data['instanceTypes'] ?: []; - $this->preferences = $data['preferences'] ?: []; + $this->arguments = isset($data['arguments']) && is_array($data['arguments']) + ? $data['arguments'] : []; + $this->virtualTypes = isset($data['instanceTypes']) && is_array($data['instanceTypes']) + ? $data['instanceTypes'] : []; + $this->preferences = isset($data['preferences']) && is_array($data['preferences']) + ? $data['preferences'] : []; } /** @@ -134,13 +135,13 @@ public function getPreference($type) */ public function extend(array $configuration) { - $this->arguments = isset($configuration['arguments']) + $this->arguments = isset($configuration['arguments']) && is_array($configuration['arguments']) ? array_replace($this->arguments, $configuration['arguments']) : $this->arguments; - $this->virtualTypes = isset($configuration['instanceTypes']) + $this->virtualTypes = isset($configuration['instanceTypes']) && is_array($configuration['instanceTypes']) ? array_replace($this->virtualTypes, $configuration['instanceTypes']) : $this->virtualTypes; - $this->preferences = isset($configuration['preferences']) + $this->preferences = isset($configuration['preferences']) && is_array($configuration['preferences']) ? array_replace($this->preferences, $configuration['preferences']) : $this->preferences; } diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php index 14169668a3b03..94df7d146c759 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php @@ -165,4 +165,122 @@ public function testGetArgumentsNotDefined() { $this->assertSame(null, $this->compiled->getArguments('class_not_stored_in_config')); } + + /** + * Test that $arguments, $virtualTypes and $preferences initializing in construct must be array. + * + * @param $data + * @param array $expectedResult + * + * @dataProvider constructorFieldsValidation + */ + public function testConstructorFieldsValidation($data, $expectedResult) + { + /** @var Compiled $compiled */ + $compiled = $this->objectManager->getObject( + Compiled::class, + [ + 'data' => $data + ] + ); + + $reflection = new \ReflectionClass(Compiled::class); + $arguments = $reflection->getProperty('arguments'); + $arguments->setAccessible(true); + + $this->assertEquals($expectedResult['arguments'], $arguments->getValue($compiled)); + $this->assertEquals($expectedResult['preferences'], $compiled->getPreferences()); + $this->assertEquals($expectedResult['instanceTypes'], $compiled->getVirtualTypes()); + } + + /** + * Data provider for testConstructorFieldsValidation. + * + * @return array + */ + public function constructorFieldsValidation() + { + return [ + [ + 'no array', + [ + 'arguments' => [], + 'instanceTypes' => [], + 'preferences' => [] + ] + ], + [ + [ + 'arguments' => 1, + 'instanceTypes' => [1, 2, 3], + 'preferences' => 'test' + ], + [ + 'arguments' => [], + 'instanceTypes' => [1, 2, 3], + 'preferences' => [] + ] + ] + ]; + } + + /** + * Test that $arguments, $virtualTypes and $preferences initializing in extend must be array. + * + * @param $data + * @param array $expectedResult + * + * @dataProvider extendFieldsValidation + */ + public function testExtendFieldsValidation($data, $expectedResult) + { + /** @var Compiled $compiled */ + $compiled = $this->objectManager->getObject( + Compiled::class, + [ + 'data' => $data + ] + ); + + $compiled->extend($data); + + $reflection = new \ReflectionClass(Compiled::class); + $arguments = $reflection->getProperty('arguments'); + $arguments->setAccessible(true); + + $this->assertEquals($expectedResult['arguments'], $arguments->getValue($compiled)); + $this->assertEquals($expectedResult['preferences'], $compiled->getPreferences()); + $this->assertEquals($expectedResult['instanceTypes'], $compiled->getVirtualTypes()); + } + + /** + * Data provider for testExtendFieldsValidation. + * + * @return array + */ + public function extendFieldsValidation() + { + return [ + [ + [], + [ + 'arguments' => [], + 'instanceTypes' => [], + 'preferences' => [] + ] + ], + [ + [ + 'arguments' => 1, + 'instanceTypes' => [1, 2, 3], + 'preferences' => 'test' + ], + [ + 'arguments' => [], + 'instanceTypes' => [1, 2, 3], + 'preferences' => [] + ] + ] + ]; + } } From 8f75141d503a88fee1763ea289d2e5db9f174180 Mon Sep 17 00:00:00 2001 From: Oleksandr Shmyheliuk Date: Thu, 22 Jun 2017 09:16:12 +0300 Subject: [PATCH 28/39] MAGETWO-70061: Exception on EE installation on environment with default-storage-engine=MyISAM --- .../Magento/Framework/DB/Ddl/Sequence.php | 24 ++----------------- .../DB/Test/Unit/Ddl/SequenceTest.php | 11 ++++----- 2 files changed, 6 insertions(+), 29 deletions(-) diff --git a/lib/internal/Magento/Framework/DB/Ddl/Sequence.php b/lib/internal/Magento/Framework/DB/Ddl/Sequence.php index 98bf0cc5ba50b..278c8ab4773c2 100644 --- a/lib/internal/Magento/Framework/DB/Ddl/Sequence.php +++ b/lib/internal/Magento/Framework/DB/Ddl/Sequence.php @@ -10,26 +10,6 @@ */ class Sequence { - /** - * Default table engine for sequences tables. - */ - const DEFAULT_ENGINE = 'INNODB'; - - /** - * Database table engine for creating sequence table. - * - * @var string - */ - private $dbEngine; - - /** - * @param null $dbEngine The database table engine - */ - public function __construct($dbEngine = null) - { - $this->dbEngine = $dbEngine ?: self::DEFAULT_ENGINE; - } - /** * Return SQL for create sequence * @@ -48,9 +28,9 @@ public function getCreateSequenceDdl( $format = "CREATE TABLE %s ( sequence_value %s %s NOT NULL AUTO_INCREMENT, PRIMARY KEY (sequence_value) - ) AUTO_INCREMENT = %d ENGINE = %s"; + ) AUTO_INCREMENT = %d ENGINE = INNODB"; - return sprintf($format, $name, $columnType, $unsigned ? 'UNSIGNED' : '', $startNumber, $this->dbEngine); + return sprintf($format, $name, $columnType, $unsigned ? 'UNSIGNED' : '', $startNumber); } /** diff --git a/lib/internal/Magento/Framework/DB/Test/Unit/Ddl/SequenceTest.php b/lib/internal/Magento/Framework/DB/Test/Unit/Ddl/SequenceTest.php index e99e54222973d..b87833d90d6a3 100644 --- a/lib/internal/Magento/Framework/DB/Test/Unit/Ddl/SequenceTest.php +++ b/lib/internal/Magento/Framework/DB/Test/Unit/Ddl/SequenceTest.php @@ -12,14 +12,13 @@ class SequenceTest extends \PHPUnit_Framework_TestCase { /** * @param array $params - * @param string $engine * @param string $expectedQuery * @dataProvider createSequenceDdlDataProvider */ - public function testGetCreateSequenceDdl(array $params, $engine, $expectedQuery) + public function testGetCreateSequenceDdl(array $params, $expectedQuery) { - $model = new Sequence($engine); - $actualQuery = call_user_func_array([$model, 'getCreateSequenceDdl'], $params); + $model = new Sequence(); + $actualQuery = $model->getCreateSequenceDdl(...array_values($params)); $cleanString = function ($string) { return trim(preg_replace('/\s+/', ' ', $string)); @@ -46,7 +45,6 @@ public function createSequenceDdlDataProvider() [ 'name' => 'someName' ], - null, 'CREATE TABLE someName ( sequence_value integer UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY (sequence_value) @@ -59,11 +57,10 @@ public function createSequenceDdlDataProvider() 'columnType' => Table::TYPE_BIGINT, 'unsigned' => false ], - 'someEngine', 'CREATE TABLE someName ( sequence_value bigint NOT NULL AUTO_INCREMENT, PRIMARY KEY (sequence_value) - ) AUTO_INCREMENT = 123 ENGINE = someEngine' + ) AUTO_INCREMENT = 123 ENGINE = INNODB' ] ]; } From 3d282f213dd5aeb2bd3759e7ca05ea2550ca3247 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Thu, 22 Jun 2017 12:26:34 +0300 Subject: [PATCH 29/39] MAGETWO-65422: Write default configs to shared configuration file by app:config:dump --- .../Config/Model/PreparedValueFactory.php | 34 +++++++--- .../Unit/Model/PreparedValueFactoryTest.php | 65 ++++++++++++++++++- .../Store/Model/ScopeTypeNormalizer.php | 63 ++++++++++++++++++ .../Unit/Model/ScopeTypeNormalizerTest.php | 59 +++++++++++++++++ 4 files changed, 210 insertions(+), 11 deletions(-) create mode 100644 app/code/Magento/Store/Model/ScopeTypeNormalizer.php create mode 100644 app/code/Magento/Store/Test/Unit/Model/ScopeTypeNormalizerTest.php diff --git a/app/code/Magento/Config/Model/PreparedValueFactory.php b/app/code/Magento/Config/Model/PreparedValueFactory.php index 842b1dff002bc..90b20707e14b1 100644 --- a/app/code/Magento/Config/Model/PreparedValueFactory.php +++ b/app/code/Magento/Config/Model/PreparedValueFactory.php @@ -12,7 +12,7 @@ use Magento\Framework\App\Config\ValueInterface; use Magento\Framework\App\Config\Value; use Magento\Framework\App\ScopeInterface; -use Magento\Store\Model\ScopeInterface as StoreScopeInterface; +use Magento\Store\Model\ScopeTypeNormalizer; use Magento\Framework\App\ScopeResolverPool; use Magento\Framework\Exception\RuntimeException; @@ -52,22 +52,32 @@ class PreparedValueFactory */ private $config; + /** + * The scope type normalizer. + * + * @var ScopeTypeNormalizer + */ + private $scopeTypeNormalizer; + /** * @param ScopeResolverPool $scopeResolverPool The scope resolver pool * @param StructureFactory $structureFactory The manager for system configuration structure * @param BackendFactory $valueFactory The factory for configuration value objects * @param ScopeConfigInterface $config The scope configuration + * @param ScopeTypeNormalizer $scopeTypeNormalizer The scope type normalizer */ public function __construct( ScopeResolverPool $scopeResolverPool, StructureFactory $structureFactory, BackendFactory $valueFactory, - ScopeConfigInterface $config + ScopeConfigInterface $config, + ScopeTypeNormalizer $scopeTypeNormalizer ) { $this->scopeResolverPool = $scopeResolverPool; $this->structureFactory = $structureFactory; $this->valueFactory = $valueFactory; $this->config = $config; + $this->scopeTypeNormalizer = $scopeTypeNormalizer; } /** @@ -106,20 +116,26 @@ public function create($path, $value, $scope, $scopeCode = null) if ($backendModel instanceof Value) { $scopeId = 0; - if (in_array($scope, [StoreScopeInterface::SCOPE_WEBSITE, StoreScopeInterface::SCOPE_WEBSITES])) { - $scope = StoreScopeInterface::SCOPE_WEBSITES; - } elseif (in_array($scope, [StoreScopeInterface::SCOPE_STORE, StoreScopeInterface::SCOPE_STORES])) { - $scope = StoreScopeInterface::SCOPE_STORES; - } + $scope = $this->scopeTypeNormalizer->normalize($scope); if ($scope !== ScopeInterface::SCOPE_DEFAULT) { - $scopeResolver = $this->scopeResolverPool->get($scope); - $scopeId = $scopeResolver->getScope($scopeCode)->getId(); + $scopeId = $this->scopeResolverPool->get($scope) + ->getScope($scopeCode) + ->getId(); + } + + if ($field instanceof Structure\Element\Field) { + $groupPath = $field->getGroupPath(); + $group = $structure->getElement($groupPath); + $backendModel->setField($field->getId()); + $backendModel->setGroupId($group->getId()); + $backendModel->setFieldConfig($field->getData()); } $backendModel->setPath($configPath); $backendModel->setScope($scope); $backendModel->setScopeId($scopeId); + $backendModel->setScopeCode($scopeCode); $backendModel->setValue($value); } diff --git a/app/code/Magento/Config/Test/Unit/Model/PreparedValueFactoryTest.php b/app/code/Magento/Config/Test/Unit/Model/PreparedValueFactoryTest.php index d0b7c40bdd809..2dfff40024106 100644 --- a/app/code/Magento/Config/Test/Unit/Model/PreparedValueFactoryTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/PreparedValueFactoryTest.php @@ -12,15 +12,18 @@ use Magento\Framework\App\Config\Value; use Magento\Config\Model\Config\Structure; use Magento\Config\Model\Config\Structure\Element\Field; +use Magento\Config\Model\Config\Structure\Element\Group; use Magento\Framework\App\ScopeInterface; use Magento\Store\Model\ScopeInterface as StoreScopeInterface; use Magento\Framework\App\ScopeResolver; use Magento\Framework\App\ScopeResolverPool; +use Magento\Store\Model\ScopeTypeNormalizer; use PHPUnit_Framework_MockObject_MockObject as Mock; /** * @inheritdoc * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ class PreparedValueFactoryTest extends \PHPUnit_Framework_TestCase { @@ -69,6 +72,11 @@ class PreparedValueFactoryTest extends \PHPUnit_Framework_TestCase */ private $scopeMock; + /** + * @var ScopeTypeNormalizer|Mock + */ + private $scopeTypeNormalizer; + /** * @var PreparedValueFactory */ @@ -91,11 +99,15 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); $this->fieldMock = $this->getMockBuilder(Field::class) + //->setMethods(['getData', 'getConfigPath', 'getId', 'hasBackendModel']) ->disableOriginalConstructor() ->getMock(); $this->valueMock = $this->getMockBuilder(Value::class) ->disableOriginalConstructor() - ->setMethods(['setPath', 'setScope', 'setScopeId', 'setValue']) + ->setMethods([ + 'setPath', 'setScope', 'setScopeId', 'setValue', 'setField', + 'setGroupId', 'setFieldConfig', 'setScopeCode' + ]) ->getMock(); $this->configMock = $this->getMockBuilder(ScopeConfigInterface::class) ->getMockForAbstractClass(); @@ -107,12 +119,16 @@ protected function setUp() ->getMock(); $this->scopeMock = $this->getMockBuilder(ScopeInterface::class) ->getMockForAbstractClass(); + $this->scopeTypeNormalizer = $this->getMockBuilder(ScopeTypeNormalizer::class) + ->disableOriginalConstructor() + ->getMock(); $this->preparedValueFactory = new PreparedValueFactory( $this->scopeResolverPoolMock, $this->structureFactoryMock, $this->valueFactoryMock, - $this->configMock + $this->configMock, + $this->scopeTypeNormalizer ); } @@ -133,6 +149,11 @@ public function testCreate( $scopeCode, $scopeId ) { + $groupPath = 'some/group'; + $groupId = 'some_group'; + $fieldId = 'some_field'; + $fieldData = ['backend_model' => 'some_model']; + if (ScopeInterface::SCOPE_DEFAULT !== $scope) { $this->scopeResolverPoolMock->expects($this->once()) ->method('get') @@ -146,19 +167,43 @@ public function testCreate( ->method('getId') ->willReturn($scopeId); } + /** @var Group|Mock $groupMock */ + $groupMock = $this->getMockBuilder(Group::class) + ->disableOriginalConstructor() + ->getMock(); + $groupMock->expects($this->once()) + ->method('getId') + ->willReturn($groupId); + $this->scopeTypeNormalizer->expects($this->once()) + ->method('normalize') + ->with($scope, true) + ->willReturnArgument(0); $this->structureFactoryMock->expects($this->once()) ->method('create') ->willReturn($this->structureMock); $this->structureMock->expects($this->once()) ->method('getElementByConfigPath') ->willReturn($this->fieldMock); + $this->structureMock->expects($this->once()) + ->method('getElement') + ->with($groupPath) + ->willReturn($groupMock); $this->fieldMock->expects($this->once()) ->method('hasBackendModel') ->willReturn(true); $this->fieldMock ->method('getConfigPath') ->willReturn($configPath); + $this->fieldMock + ->method('getId') + ->willReturn($fieldId); + $this->fieldMock + ->method('getData') + ->willReturn($fieldData); + $this->fieldMock->expects($this->once()) + ->method('getGroupPath') + ->willReturn($groupPath); $this->valueFactoryMock->expects($this->once()) ->method('create') ->willReturn($this->valueMock); @@ -174,10 +219,26 @@ public function testCreate( ->method('setScopeId') ->with($scopeId) ->willReturnSelf(); + $this->valueMock->expects($this->once()) + ->method('setScopeCode') + ->with($scopeCode) + ->willReturnSelf(); $this->valueMock->expects($this->once()) ->method('setValue') ->with($value) ->willReturnSelf(); + $this->valueMock->expects($this->once()) + ->method('setField') + ->with($fieldId) + ->willReturnSelf(); + $this->valueMock->expects($this->once()) + ->method('setGroupId') + ->with($groupId) + ->willReturnSelf(); + $this->valueMock->expects($this->once()) + ->method('setFieldConfig') + ->with($fieldData) + ->willReturnSelf(); $this->assertInstanceOf( Value::class, diff --git a/app/code/Magento/Store/Model/ScopeTypeNormalizer.php b/app/code/Magento/Store/Model/ScopeTypeNormalizer.php new file mode 100644 index 0000000000000..87a3c7064f293 --- /dev/null +++ b/app/code/Magento/Store/Model/ScopeTypeNormalizer.php @@ -0,0 +1,63 @@ +normalize('websites', false); + * $this->normalize('website', false); + * ``` + * + * This calls of this method returns 'websites': + * ```php + * $this->normalize('website', false); + * $this->normalize('websites', false); + * $this->normalize('website'); + * $this->normalize('websites'); + * ``` + * + * If there is not scope in the list (websites, website, groups, group, stores, store) + * then it will be returned without changes. + * + * The next calls of this method returns 'default': + * ```php + * $this->normalize('default', false); + * $this->normalize('default', true); + * $this->normalize('default'); + * ``` + * + * @param string $scopeType The type of scope + * @param bool $plural The flag for choosing returned form of scope, in the plural or not. Used plural by default. + * @return string + */ + public function normalize($scopeType, $plural = true) + { + $replaces = [ + ScopeInterface::SCOPE_WEBSITE => ScopeInterface::SCOPE_WEBSITES, + ScopeInterface::SCOPE_GROUP => ScopeInterface::SCOPE_GROUPS, + ScopeInterface::SCOPE_STORE => ScopeInterface::SCOPE_STORES, + ]; + + if (!$plural) { + $replaces = array_flip($replaces); + } + + return $replaces[$scopeType] ?? $scopeType; + } +} diff --git a/app/code/Magento/Store/Test/Unit/Model/ScopeTypeNormalizerTest.php b/app/code/Magento/Store/Test/Unit/Model/ScopeTypeNormalizerTest.php new file mode 100644 index 0000000000000..37791993849a0 --- /dev/null +++ b/app/code/Magento/Store/Test/Unit/Model/ScopeTypeNormalizerTest.php @@ -0,0 +1,59 @@ +scopeTypeNormalizer = new ScopeTypeNormalizer(); + } + + /** + * @param string $scopeType + * @param bool $plural + * @param string $expectedResult + * @dataProvider normalizeDataProvider + */ + public function testNormalize($scopeType, $plural, $expectedResult) + { + $this->assertEquals($expectedResult, $this->scopeTypeNormalizer->normalize($scopeType, $plural)); + } + + /** + * @return array + */ + public function normalizeDataProvider() + { + return [ + [ScopeInterface::SCOPE_WEBSITE, true, ScopeInterface::SCOPE_WEBSITES], + [ScopeInterface::SCOPE_WEBSITES, true, ScopeInterface::SCOPE_WEBSITES], + [ScopeInterface::SCOPE_WEBSITE, false, ScopeInterface::SCOPE_WEBSITE], + [ScopeInterface::SCOPE_WEBSITES, false, ScopeInterface::SCOPE_WEBSITE], + [ScopeInterface::SCOPE_GROUP, true, ScopeInterface::SCOPE_GROUPS], + [ScopeInterface::SCOPE_GROUPS, true, ScopeInterface::SCOPE_GROUPS], + [ScopeInterface::SCOPE_GROUP, false, ScopeInterface::SCOPE_GROUP], + [ScopeInterface::SCOPE_GROUPS, false, ScopeInterface::SCOPE_GROUP], + [ScopeInterface::SCOPE_STORE, true, ScopeInterface::SCOPE_STORES], + [ScopeInterface::SCOPE_STORES, true, ScopeInterface::SCOPE_STORES], + [ScopeInterface::SCOPE_STORE, false, ScopeInterface::SCOPE_STORE], + [ScopeInterface::SCOPE_STORES, false, ScopeInterface::SCOPE_STORE], + ['default', true, 'default'], + ['default', false, 'default'], + ]; + } +} From 02dfbe499bcb5df40250691fc36de7843e44a4f0 Mon Sep 17 00:00:00 2001 From: RomanKis Date: Thu, 22 Jun 2017 14:03:45 +0300 Subject: [PATCH 30/39] MAGETWO-60533: Data passed to Magento\Framework\ObjectManager\Config\Compiled is not validated --- .../Test/Unit/Config/CompiledTest.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php index 94df7d146c759..bbaf9a3301e08 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php @@ -180,7 +180,7 @@ public function testConstructorFieldsValidation($data, $expectedResult) $compiled = $this->objectManager->getObject( Compiled::class, [ - 'data' => $data + 'data' => $data, ] ); @@ -206,19 +206,19 @@ public function constructorFieldsValidation() [ 'arguments' => [], 'instanceTypes' => [], - 'preferences' => [] + 'preferences' => [], ] ], [ [ 'arguments' => 1, 'instanceTypes' => [1, 2, 3], - 'preferences' => 'test' + 'preferences' => 'test', ], [ 'arguments' => [], 'instanceTypes' => [1, 2, 3], - 'preferences' => [] + 'preferences' => [], ] ] ]; @@ -238,7 +238,7 @@ public function testExtendFieldsValidation($data, $expectedResult) $compiled = $this->objectManager->getObject( Compiled::class, [ - 'data' => $data + 'data' => $data, ] ); @@ -266,19 +266,19 @@ public function extendFieldsValidation() [ 'arguments' => [], 'instanceTypes' => [], - 'preferences' => [] + 'preferences' => [], ] ], [ [ 'arguments' => 1, 'instanceTypes' => [1, 2, 3], - 'preferences' => 'test' + 'preferences' => 'test', ], [ 'arguments' => [], 'instanceTypes' => [1, 2, 3], - 'preferences' => [] + 'preferences' => [], ] ] ]; From b621c460ace7e61738d2e0f021866996d9764f99 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Thu, 22 Jun 2017 14:25:56 +0300 Subject: [PATCH 31/39] MAGETWO-65422: Write default configs to shared configuration file by app:config:dump --- app/code/Magento/Store/Model/ScopeTypeNormalizer.php | 2 +- .../Store/Test/Unit/Model/ScopeTypeNormalizerTest.php | 2 +- .../Magento/Config/Console/Command/ConfigSetCommandTest.php | 2 +- .../Deploy/Console/Command/App/ConfigImportCommandTest.php | 5 ++++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Store/Model/ScopeTypeNormalizer.php b/app/code/Magento/Store/Model/ScopeTypeNormalizer.php index 87a3c7064f293..69d5cdc39e1e6 100644 --- a/app/code/Magento/Store/Model/ScopeTypeNormalizer.php +++ b/app/code/Magento/Store/Model/ScopeTypeNormalizer.php @@ -1,6 +1,6 @@ expects($this->once()) ->method('writeln') ->with( - 'Invalid value. Value must be a URL or one of placeholders: {{base_url}}' + 'Invalid Base URL. Value must be a URL or one of placeholders: {{base_url}}' ); }, 'web/unsecure/base_url', diff --git a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ConfigImportCommandTest.php b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ConfigImportCommandTest.php index 12cbda7f3ea98..8534c0dd977a1 100644 --- a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ConfigImportCommandTest.php +++ b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/App/ConfigImportCommandTest.php @@ -287,7 +287,10 @@ public function testImportConfig() $commandTester->execute([]); - $this->assertContains('Import failed: Invalid value. Value must be', $commandTester->getDisplay()); + $this->assertContains( + 'Invalid Secure Base URL. Value must be a URL or one of placeholders: {{base_url}},{{unsecure_base_url}}', + $commandTester->getDisplay() + ); $this->assertSame(Cli::RETURN_FAILURE, $commandTester->getStatusCode()); $this->writeConfig($this->config, $wrongCurrency); From f295aa03809bbe36788bebcfd8f09d789623f7b7 Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov Date: Wed, 21 Jun 2017 19:06:17 +0300 Subject: [PATCH 32/39] MAGETWO-69260: Unable to create shipping label due to error of lb to kg conversion - Replace the `round` method by `sprintf` --- .../Dhl/Block/Adminhtml/Unitofmeasure.php | 12 +- app/code/Magento/Dhl/Model/Carrier.php | 20 +-- .../Dhl/Test/Unit/Model/CarrierTest.php | 128 +++++++++--------- .../Model/_files/rates_request_data_dhl.php | 2 +- app/code/Magento/Usps/Model/Carrier.php | 6 +- .../Usps/Test/Unit/Model/CarrierTest.php | 104 +++++++------- .../Unit/Model/_files/rates_request_data.php | 4 +- 7 files changed, 143 insertions(+), 133 deletions(-) diff --git a/app/code/Magento/Dhl/Block/Adminhtml/Unitofmeasure.php b/app/code/Magento/Dhl/Block/Adminhtml/Unitofmeasure.php index efabfb5749244..deeb769c808fa 100644 --- a/app/code/Magento/Dhl/Block/Adminhtml/Unitofmeasure.php +++ b/app/code/Magento/Dhl/Block/Adminhtml/Unitofmeasure.php @@ -71,14 +71,12 @@ public function _construct() ) ); - $weight = round( - $this->carrierHelper->convertMeasureWeight( - $kgWeight, - \Zend_Measure_Weight::KILOGRAM, - \Zend_Measure_Weight::POUND - ), - 3 + $convertedWeight = $this->carrierHelper->convertMeasureWeight( + $kgWeight, + \Zend_Measure_Weight::KILOGRAM, + \Zend_Measure_Weight::POUND ); + $weight = sprintf('%.3f', $convertedWeight); $this->setDivideOrderWeightNoteLbp( __( diff --git a/app/code/Magento/Dhl/Model/Carrier.php b/app/code/Magento/Dhl/Model/Carrier.php index 2b38c1661b6ea..0be528396d4e8 100644 --- a/app/code/Magento/Dhl/Model/Carrier.php +++ b/app/code/Magento/Dhl/Model/Carrier.php @@ -422,7 +422,7 @@ public function setRequest(\Magento\Framework\DataObject $request) $shippingWeight = $request->getPackageWeight(); - $requestObject->setValue(round($request->getPackageValue(), 2)) + $requestObject->setValue(sprintf('%.2f', $request->getPackageValue())) ->setValueWithDiscount($request->getPackageValueWithDiscount()) ->setCustomsValue($request->getPackageCustomsValue()) ->setDestStreet($this->string->substr(str_replace("\n", '', $request->getDestStreet()), 0, 35)) @@ -670,13 +670,13 @@ protected function _getWeight($weight, $maxWeight = false, $configWeightUnit = f if ($configWeightUnit != $countryWeightUnit) { $weight = $this->_carrierHelper->convertMeasureWeight( - round($weight, 3), + sprintf('%.3f', $weight), $configWeightUnit, $countryWeightUnit ); } - return round($weight, 3); + return sprintf('%.3f', $weight); } /** @@ -806,7 +806,7 @@ protected function _makePieces(\Magento\Shipping\Model\Simplexml\Element $nodeBk $nodePiece = $nodePieces->addChild('Piece', '', ''); $nodePiece->addChild('PieceID', $numberOfPieces); $this->_addDimension($nodePiece); - $nodePiece->addChild('Weight', $sumWeight); + $nodePiece->addChild('Weight', sprintf('%.3f', $sumWeight)); break; } else { unset($items[$key]); @@ -815,7 +815,7 @@ protected function _makePieces(\Magento\Shipping\Model\Simplexml\Element $nodeBk $nodePiece = $nodePieces->addChild('Piece', '', ''); $nodePiece->addChild('PieceID', $numberOfPieces); $this->_addDimension($nodePiece); - $nodePiece->addChild('Weight', $sumWeight); + $nodePiece->addChild('Weight', sprintf('%.3f', $sumWeight)); $sumWeight = 0; break; } @@ -826,7 +826,7 @@ protected function _makePieces(\Magento\Shipping\Model\Simplexml\Element $nodeBk $nodePiece = $nodePieces->addChild('Piece', '', ''); $nodePiece->addChild('PieceID', $numberOfPieces); $this->_addDimension($nodePiece); - $nodePiece->addChild('Weight', $sumWeight); + $nodePiece->addChild('Weight', sprintf('%.3f', $sumWeight)); } } else { $nodePiece = $nodePieces->addChild('Piece', '', ''); @@ -870,13 +870,13 @@ protected function _getDimension($dimension, $configWeightUnit = false) if ($configDimensionUnit != $countryDimensionUnit) { $dimension = $this->_carrierHelper->convertMeasureDimension( - round($dimension, 3), + sprintf('%.3f', $dimension), $configDimensionUnit, $countryDimensionUnit ); } - return round($dimension, 3); + return sprintf('%.3f', $dimension); } /** @@ -1614,7 +1614,7 @@ protected function _shipmentDetails($xml, $rawRequest, $originRegion = '') } $nodePiece->addChild('PieceID', ++$i); $nodePiece->addChild('PackageType', $packageType); - $nodePiece->addChild('Weight', round($package['params']['weight'], 1)); + $nodePiece->addChild('Weight', sprintf('%.1f', $package['params']['weight'])); $params = $package['params']; if ($params['width'] && $params['length'] && $params['height']) { if (!$originRegion) { @@ -1635,7 +1635,7 @@ protected function _shipmentDetails($xml, $rawRequest, $originRegion = '') } if (!$originRegion) { - $nodeShipmentDetails->addChild('Weight', round($rawRequest->getPackageWeight(), 1)); + $nodeShipmentDetails->addChild('Weight', sprintf('%.1f', $rawRequest->getPackageWeight())); $nodeShipmentDetails->addChild('WeightUnit', substr($this->_getWeightUnit(), 0, 1)); $nodeShipmentDetails->addChild('GlobalProductCode', $rawRequest->getShippingMethod()); $nodeShipmentDetails->addChild('LocalProductCode', $rawRequest->getShippingMethod()); diff --git a/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php b/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php index 5035fb987567e..0dc04ba4fbaca 100644 --- a/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php +++ b/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php @@ -5,8 +5,13 @@ */ namespace Magento\Dhl\Test\Unit\Model; -use Magento\Quote\Model\Quote\Address\RateRequest; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\HTTP\ZendClient; +use Magento\Framework\HTTP\ZendClientFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\Xml\Security; +use Magento\Quote\Model\Quote\Address\RateRequest; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -14,39 +19,39 @@ class CarrierTest extends \PHPUnit_Framework_TestCase { /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var ObjectManager */ - protected $_helper; + private $objectManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Zend_Http_Response|MockObject */ - protected $_httpResponse; + private $httpResponse; /** * @var \Magento\Dhl\Model\Carrier */ - protected $_model; + private $model; /** - * @var \Magento\Quote\Model\Quote\Address\RateResult\Error|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Quote\Model\Quote\Address\RateResult\Error|MockObject */ - protected $error; + private $error; /** - * @var \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory|MockObject */ - protected $errorFactory; + private $errorFactory; /** - * @var \Magento\Dhl\Model\Carrier|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - protected $carrier; + private $scope; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ZendClient|MockObject */ - protected $scope; + private $httpClient; /** * @return void @@ -54,19 +59,11 @@ class CarrierTest extends \PHPUnit_Framework_TestCase */ protected function setUp() { - $this->_helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->objectManager = new ObjectManager($this); - $this->scope = $this->getMockBuilder( - \Magento\Framework\App\Config\ScopeConfigInterface::class - )->disableOriginalConstructor()->getMock(); - - $this->scope->expects( - $this->any() - )->method( - 'getValue' - )->will( - $this->returnCallback([$this, 'scopeConfiggetValue']) - ); + $this->scope = $this->getMockForAbstractClass(ScopeConfigInterface::class); + $this->scope->method('getValue') + ->willReturnCallback([$this, 'scopeConfigGetValue']); // xml element factory $xmlElFactory = $this->getMockBuilder( @@ -77,7 +74,7 @@ protected function setUp() $xmlElFactory->expects($this->any())->method('create')->will( $this->returnCallback( function ($data) { - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $helper = new ObjectManager($this); return $helper->getObject( \Magento\Shipping\Model\Simplexml\Element::class, @@ -115,27 +112,23 @@ function ($data) { $rateMethodFactory->expects($this->any())->method('create')->will($this->returnValue($rateMethod)); - // http client - $this->_httpResponse = $this->getMockBuilder( - \Zend_Http_Response::class - )->disableOriginalConstructor()->setMethods( - ['getBody'] - )->getMock(); + $this->httpResponse = $this->getMockBuilder(\Zend_Http_Response::class) + ->disableOriginalConstructor() + ->getMock(); - $httpClient = $this->getMockBuilder( - \Magento\Framework\HTTP\ZendClient::class - )->disableOriginalConstructor()->setMethods( - ['request'] - )->getMock(); - $httpClient->expects($this->any())->method('request')->will($this->returnValue($this->_httpResponse)); + $this->httpClient = $this->getMockBuilder(ZendClient::class) + ->disableOriginalConstructor() + ->setMethods(['request']) + ->getMock(); + $this->httpClient->method('request') + ->willReturn($this->httpResponse); - $httpClientFactory = $this->getMockBuilder( - \Magento\Framework\HTTP\ZendClientFactory::class - )->disableOriginalConstructor()->setMethods( - ['create'] - )->getMock(); + $httpClientFactory = $this->getMockBuilder(ZendClientFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $httpClientFactory->method('create') + ->willReturn($this->httpClient); - $httpClientFactory->expects($this->any())->method('create')->will($this->returnValue($httpClient)); $modulesDirectory = $this->getMockBuilder( \Magento\Framework\Filesystem\Directory\Read::class )->disableOriginalConstructor()->setMethods( @@ -174,7 +167,7 @@ function ($data) { $this->errorFactory->expects($this->any())->method('create')->willReturn($this->error); - $this->_model = $this->_helper->getObject( + $this->model = $this->objectManager->getObject( \Magento\Dhl\Model\Carrier::class, [ 'scopeConfig' => $this->scope, @@ -192,11 +185,12 @@ function ($data) { } /** - * Callback function, emulates getValue function - * @param $path - * @return null|string + * Emulates the config's `getValue` method. + * + * @param string $path + * @return string|null */ - public function scopeConfiggetValue($path) + public function scopeConfigGetValue($path) { $pathMap = [ 'carriers/dhl/shipment_days' => 'Mon,Tue,Wed,Thu,Fri,Sat', @@ -211,6 +205,7 @@ public function scopeConfiggetValue($path) 'carriers/dhl/showmethod' => 1, 'carriers/dhl/title' => 'dhl Title', 'carriers/dhl/specificerrmsg' => 'dhl error message', + 'carriers/dhl/unit_of_measure' => 'L', ]; return isset($pathMap[$path]) ? $pathMap[$path] : null; } @@ -260,7 +255,7 @@ public function prepareShippingLabelContentExceptionDataProvider() */ protected function _invokePrepareShippingLabelContent(\SimpleXMLElement $xml) { - $model = $this->_helper->getObject(\Magento\Dhl\Model\Carrier::class); + $model = $this->objectManager->getObject(\Magento\Dhl\Model\Carrier::class); $method = new \ReflectionMethod($model, '_prepareShippingLabelContent'); $method->setAccessible(true); return $method->invoke($model, $xml); @@ -268,21 +263,24 @@ protected function _invokePrepareShippingLabelContent(\SimpleXMLElement $xml) public function testCollectRates() { - $this->scope->expects($this->any())->method('isSetFlag')->willReturn(true); + $this->scope->method('isSetFlag') + ->willReturn(true); - $this->_httpResponse->expects( - $this->any() - )->method( - 'getBody' - )->will( - $this->returnValue(file_get_contents(__DIR__ . '/_files/success_dhl_response_rates.xml')) - ); - // for setRequest - $request = $this->_helper->getObject( - \Magento\Quote\Model\Quote\Address\RateRequest::class, + $this->httpResponse->method('getBody') + ->willReturn(file_get_contents(__DIR__ . '/_files/success_dhl_response_rates.xml')); + + /** @var RateRequest $request */ + $request = $this->objectManager->getObject( + RateRequest::class, require __DIR__ . '/_files/rates_request_data_dhl.php' ); - $this->assertNotEmpty($this->_model->collectRates($request)->getAllRates()); + + $reflectionClass = new \ReflectionObject($this->httpClient); + $rawPostData = $reflectionClass->getProperty('raw_post_data'); + $rawPostData->setAccessible(true); + + self::assertNotEmpty($this->model->collectRates($request)->getAllRates()); + self::assertContains('8.266', $rawPostData->getValue($this->httpClient)); } public function testCollectRatesErrorMessage() @@ -296,7 +294,7 @@ public function testCollectRatesErrorMessage() $request = new RateRequest(); $request->setPackageWeight(1); - $this->assertSame($this->error, $this->_model->collectRates($request)); + $this->assertSame($this->error, $this->model->collectRates($request)); } public function testCollectRatesFail() @@ -306,6 +304,6 @@ public function testCollectRatesFail() $request = new RateRequest(); $request->setPackageWeight(1); - $this->assertFalse(false, $this->_model->collectRates($request)); + $this->assertFalse(false, $this->model->collectRates($request)); } } diff --git a/app/code/Magento/Dhl/Test/Unit/Model/_files/rates_request_data_dhl.php b/app/code/Magento/Dhl/Test/Unit/Model/_files/rates_request_data_dhl.php index 411974835c61e..32e9c78886cef 100644 --- a/app/code/Magento/Dhl/Test/Unit/Model/_files/rates_request_data_dhl.php +++ b/app/code/Magento/Dhl/Test/Unit/Model/_files/rates_request_data_dhl.php @@ -14,7 +14,7 @@ 'dest_postal' => '10559', 'package_value' => '5', 'package_value_with_discount' => '5', - 'package_weight' => '5', + 'package_weight' => '8.2657', 'package_qty' => '1', 'package_physical_value' => '5', 'free_method_weight' => '5', diff --git a/app/code/Magento/Usps/Model/Carrier.php b/app/code/Magento/Usps/Model/Carrier.php index bec27bea7f50e..0e50f1d8134ba 100644 --- a/app/code/Magento/Usps/Model/Carrier.php +++ b/app/code/Magento/Usps/Model/Carrier.php @@ -340,7 +340,8 @@ public function setRequest(\Magento\Quote\Model\Quote\Address\RateRequest $reque $weight = $this->getTotalNumOfBoxes($request->getPackageWeight()); $r->setWeightPounds(floor($weight)); - $r->setWeightOunces(round(($weight - floor($weight)) * self::OUNCES_POUND, self::$weightPrecision)); + $ounces = ($weight - floor($weight)) * self::OUNCES_POUND; + $r->setWeightOunces(sprintf('%.' . self::$weightPrecision . 'f', $ounces)); if ($request->getFreeMethodWeight() != $request->getPackageWeight()) { $r->setFreeMethodWeight($request->getFreeMethodWeight()); } @@ -387,7 +388,8 @@ protected function _setFreeMethodRequest($freeMethod) $weight = $this->getTotalNumOfBoxes($r->getFreeMethodWeight()); $r->setWeightPounds(floor($weight)); - $r->setWeightOunces(round(($weight - floor($weight)) * self::OUNCES_POUND, self::$weightPrecision)); + $ounces = ($weight - floor($weight)) * self::OUNCES_POUND; + $r->setWeightOunces(sprintf('%.' . self::$weightPrecision . 'f', $ounces)); $r->setService($freeMethod); } diff --git a/app/code/Magento/Usps/Test/Unit/Model/CarrierTest.php b/app/code/Magento/Usps/Test/Unit/Model/CarrierTest.php index 462aaa203fe46..fb6b68ef1da7e 100644 --- a/app/code/Magento/Usps/Test/Unit/Model/CarrierTest.php +++ b/app/code/Magento/Usps/Test/Unit/Model/CarrierTest.php @@ -5,9 +5,12 @@ */ namespace Magento\Usps\Test\Unit\Model; +use Magento\Framework\HTTP\ZendClient; +use Magento\Framework\HTTP\ZendClientFactory; use Magento\Quote\Model\Quote\Address\RateRequest; use Magento\Usps\Helper\Data as DataHelper; use Magento\Usps\Model\Carrier; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -15,46 +18,51 @@ class CarrierTest extends \PHPUnit_Framework_TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Zend_Http_Response|MockObject */ - protected $httpResponse; + private $httpResponse; /** * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ - protected $helper; + private $objectManager; /** - * @var \Magento\Quote\Model\Quote\Address\RateResult\Error|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Quote\Model\Quote\Address\RateResult\Error|MockObject */ - protected $error; + private $error; /** - * @var \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory|MockObject */ - protected $errorFactory; + private $errorFactory; /** - * @var \Magento\Usps\Model\Carrier|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Usps\Model\Carrier|MockObject */ - protected $carrier; + private $carrier; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Config\ScopeConfigInterface|MockObject */ - protected $scope; + private $scope; /** - * @var DataHelper|\PHPUnit_Framework_MockObject_MockObject + * @var DataHelper|MockObject */ private $dataHelper; + /** + * @var ZendClient|MockObject + */ + private $httpClient; + /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function setUp() { - $this->helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->scope = $this->getMockBuilder( \Magento\Framework\App\Config\ScopeConfigInterface::class @@ -121,19 +129,16 @@ function ($data) { ['getBody'] )->getMock(); - $httpClient = $this->getMockBuilder( - \Magento\Framework\HTTP\ZendClient::class - )->disableOriginalConstructor()->setMethods( - ['request'] - )->getMock(); - $httpClient->expects($this->any())->method('request')->will($this->returnValue($this->httpResponse)); + $this->httpClient = $this->getMockBuilder(ZendClient::class) + ->getMock(); + $this->httpClient->method('request') + ->willReturn($this->httpResponse); - $httpClientFactory = $this->getMockBuilder( - \Magento\Framework\HTTP\ZendClientFactory::class - )->disableOriginalConstructor()->setMethods( - ['create'] - )->getMock(); - $httpClientFactory->expects($this->any())->method('create')->will($this->returnValue($httpClient)); + $httpClientFactory = $this->getMockBuilder(ZendClientFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $httpClientFactory->method('create') + ->willReturn($this->httpClient); $data = ['id' => 'usps', 'store' => '1']; @@ -165,9 +170,9 @@ function ($data) { ->setMethods(['displayGirthValue']) ->getMock(); - $this->carrier = $this->helper->getObject(\Magento\Usps\Model\Carrier::class, $arguments); + $this->carrier = $this->objectManager->getObject(\Magento\Usps\Model\Carrier::class, $arguments); - $this->helper->setBackwardCompatibleProperty($this->carrier, 'dataHelper', $this->dataHelper); + $this->objectManager->setBackwardCompatibleProperty($this->carrier, 'dataHelper', $this->dataHelper); } /** @@ -185,23 +190,29 @@ public function testGetCodeBool() public function testCollectRates() { - $this->scope->expects($this->any())->method('isSetFlag')->will($this->returnValue(true)); + $expectedRequest = '' + . '2ALL' + . '9003244.2512000000' + . 'VARIABLEREGULAR'; + $expectedXml = new \SimpleXMLElement($expectedRequest); + + $this->scope->method('isSetFlag') + ->willReturn(true); + + $this->httpClient->expects(self::exactly(2)) + ->method('setParameterGet') + ->withConsecutive( + ['API', 'RateV4'], + ['XML', self::equalTo($expectedXml->asXML())] + ); + + $this->httpResponse->method('getBody') + ->willReturn(file_get_contents(__DIR__ . '/_files/success_usps_response_rates.xml')); - $this->httpResponse->expects( - $this->any() - )->method( - 'getBody' - )->will( - $this->returnValue(file_get_contents(__DIR__ . '/_files/success_usps_response_rates.xml')) - ); - // for setRequest $data = require __DIR__ . '/_files/rates_request_data.php'; - $request = $this->helper->getObject( - \Magento\Quote\Model\Quote\Address\RateRequest::class, - ['data' => $data[0]] - ); + $request = $this->objectManager->getObject(RateRequest::class, ['data' => $data[0]]); - $this->assertNotEmpty($this->carrier->collectRates($request)->getAllRates()); + self::assertNotEmpty($this->carrier->collectRates($request)->getAllRates()); } public function testCollectRatesWithUnavailableService() @@ -217,7 +228,7 @@ public function testCollectRatesWithUnavailableService() ->willReturn(file_get_contents(__DIR__ . '/_files/response_rates.xml')); $data = require __DIR__ . '/_files/rates_request_data.php'; - $request = $this->helper->getObject(RateRequest::class, ['data' => $data[1]]); + $request = $this->objectManager->getObject(RateRequest::class, ['data' => $data[1]]); $rates = $this->carrier->collectRates($request)->getAllRates(); static::assertEquals($expectedCount, count($rates)); } @@ -231,7 +242,7 @@ public function testReturnOfShipment() )->will( $this->returnValue(file_get_contents(__DIR__ . '/_files/success_usps_response_return_shipment.xml')) ); - $request = $this->helper->getObject( + $request = $this->objectManager->getObject( \Magento\Shipping\Model\Shipment\ReturnShipment::class, require __DIR__ . '/_files/return_shipment_request_data.php' ); @@ -239,11 +250,12 @@ public function testReturnOfShipment() } /** - * Callback function, emulates getValue function + * Emulates the config's `getValue` method. + * * @param $path - * @return null|string + * @return string|string */ - public function scopeConfiggetValue($path) + public function scopeConfigGetValue($path) { switch ($path) { case 'carriers/usps/allowed_methods': diff --git a/app/code/Magento/Usps/Test/Unit/Model/_files/rates_request_data.php b/app/code/Magento/Usps/Test/Unit/Model/_files/rates_request_data.php index b445e137f28a6..8fa7ab219eb59 100644 --- a/app/code/Magento/Usps/Test/Unit/Model/_files/rates_request_data.php +++ b/app/code/Magento/Usps/Test/Unit/Model/_files/rates_request_data.php @@ -13,7 +13,7 @@ 'dest_postcode' => '90032', 'package_value' => '5', 'package_value_with_discount' => '5', - 'package_weight' => '5', + 'package_weight' => '4.2657', 'package_qty' => '1', 'package_physical_value' => '5', 'free_method_weight' => '5', @@ -38,7 +38,7 @@ 'dest_country_id' => 'CA', 'dest_postcode' => 'M5V 3G5', 'dest_country_name' => 'Canada', - 'package_value' => '5', + 'package_value' => '3.2568', 'package_value_with_discount' => '5', 'package_weight' => '5', 'package_qty' => '1', From c5d12fc24a1ef9f775020a6e81df9423f088f194 Mon Sep 17 00:00:00 2001 From: RomanKis Date: Thu, 22 Jun 2017 17:51:40 +0300 Subject: [PATCH 33/39] MAGETWO-60533: Data passed to Magento\Framework\ObjectManager\Config\Compiled is not validated --- .../ObjectManager/Test/Unit/Config/CompiledTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php index bbaf9a3301e08..e54e0edaba96c 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php @@ -207,7 +207,7 @@ public function constructorFieldsValidation() 'arguments' => [], 'instanceTypes' => [], 'preferences' => [], - ] + ], ], [ [ @@ -219,8 +219,8 @@ public function constructorFieldsValidation() 'arguments' => [], 'instanceTypes' => [1, 2, 3], 'preferences' => [], - ] - ] + ], + ], ]; } @@ -267,7 +267,7 @@ public function extendFieldsValidation() 'arguments' => [], 'instanceTypes' => [], 'preferences' => [], - ] + ], ], [ [ @@ -279,8 +279,8 @@ public function extendFieldsValidation() 'arguments' => [], 'instanceTypes' => [1, 2, 3], 'preferences' => [], - ] - ] + ], + ], ]; } } From 4e42488aee881aca265e135c8f5692f1321097ee Mon Sep 17 00:00:00 2001 From: nmalevanec Date: Fri, 23 Jun 2017 10:50:23 +0300 Subject: [PATCH 34/39] MAGETWO-58961: Make Mysql/AdapterSest more extendable. --- .../Magento/Framework/Search/Adapter/Mysql/AdapterTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php index 09a23c0d6cf4d..55c9bbdcdb8ab 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php @@ -28,7 +28,7 @@ class AdapterTest extends \PHPUnit_Framework_TestCase /** * @var \Magento\Framework\Search\Request\Builder */ - private $requestBuilder; + protected $requestBuilder; /** * @var \Magento\Framework\ObjectManagerInterface @@ -94,7 +94,7 @@ protected function createAdapter() /** * @return \Magento\Framework\Search\Response\QueryResponse */ - private function executeQuery() + protected function executeQuery() { /** @var \Magento\Framework\Search\RequestInterface $queryRequest */ $queryRequest = $this->requestBuilder->create(); From 654453b3ea5eaedfe4b83f7982e8e6d01a7acd4d Mon Sep 17 00:00:00 2001 From: Stanislav Idolov Date: Fri, 23 Jun 2017 12:48:16 +0300 Subject: [PATCH 35/39] MAGETWO-64518: \Magento\CatalogRule\Model\Indexer\IndexBuilder method "doReindexFull()" causes temporary missing sale prices --- .../Model/Indexer/ProductPriceCalculator.php | 6 +- .../Model/Indexer/ReindexRuleGroupWebsite.php | 5 + .../Model/Indexer/ReindexRuleProduct.php | 5 + .../Model/Indexer/ReindexRuleProductPrice.php | 5 + .../Indexer/RuleProductPricesPersistor.php | 21 +- .../Indexer/RuleProductsSelectBuilder.php | 7 +- .../Indexer/ProductPriceCalculatorTest.php | 103 ++++++++++ .../Indexer/ReindexRuleGroupWebsiteTest.php | 108 ++++++++++ .../Indexer/ReindexRuleProductPriceTest.php | 128 ++++++++++++ .../Model/Indexer/ReindexRuleProductTest.php | 152 ++++++++++++++ .../RuleProductPricesPersistorTest.php | 163 +++++++++++++++ .../Indexer/RuleProductsSelectBuilderTest.php | 187 ++++++++++++++++++ .../CatalogRule/Test/Unit/Model/RuleTest.php | 6 + 13 files changed, 886 insertions(+), 10 deletions(-) create mode 100644 app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ProductPriceCalculatorTest.php create mode 100644 app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleGroupWebsiteTest.php create mode 100644 app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductPriceTest.php create mode 100644 app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductTest.php create mode 100644 app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductPricesPersistorTest.php create mode 100644 app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductsSelectBuilderTest.php diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ProductPriceCalculator.php b/app/code/Magento/CatalogRule/Model/Indexer/ProductPriceCalculator.php index 9b6e6ad285117..02b499c75b7e9 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/ProductPriceCalculator.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/ProductPriceCalculator.php @@ -6,6 +6,9 @@ namespace Magento\CatalogRule\Model\Indexer; +/** + * Product price calculation according rules settings. + */ class ProductPriceCalculator { /** @@ -14,7 +17,6 @@ class ProductPriceCalculator private $priceCurrency; /** - * ProductPriceCalculator constructor. * @param \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency */ public function __construct(\Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency) @@ -23,6 +25,8 @@ public function __construct(\Magento\Framework\Pricing\PriceCurrencyInterface $p } /** + * Calculates product price. + * * @param array $ruleData * @param null $productData * @return float diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleGroupWebsite.php b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleGroupWebsite.php index 3fd1522bd9f6a..cc5d07b18e0fa 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleGroupWebsite.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleGroupWebsite.php @@ -6,6 +6,9 @@ namespace Magento\CatalogRule\Model\Indexer; +/** + * Reindex information about rule relations with customer groups and websites. + */ class ReindexRuleGroupWebsite { /** @@ -44,6 +47,8 @@ public function __construct( } /** + * Prepare and persist information about rule relations with customer groups and websites to index table. + * * @param bool $useAdditionalTable * @return bool */ diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php index 50f10b0c60ef3..534061d593123 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php @@ -6,6 +6,9 @@ namespace Magento\CatalogRule\Model\Indexer; +/** + * Reindex rule relations with products. + */ class ReindexRuleProduct { /** @@ -31,6 +34,8 @@ public function __construct( } /** + * Reindex information about rule relations with products. + * * @param \Magento\CatalogRule\Model\Rule $rule * @param int $batchCount * @param bool $useAdditionalTable diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php index 32aba848338f5..6a87be3c50a64 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php @@ -6,6 +6,9 @@ namespace Magento\CatalogRule\Model\Indexer; +/** + * Reindex product prices according rule settings. + */ class ReindexRuleProductPrice { /** @@ -55,6 +58,8 @@ public function __construct( } /** + * Reindex product prices. + * * @param int $batchCount * @param \Magento\Catalog\Model\Product|null $product * @param bool $useAdditionalTable diff --git a/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php index c920a1dd20cf2..8682849c37c85 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php @@ -6,6 +6,9 @@ namespace Magento\CatalogRule\Model\Indexer; +/** + * Persist product prices to index table. + */ class RuleProductPricesPersistor { /** @@ -39,14 +42,16 @@ public function __construct( } /** - * @param array $arrData + * Persist prices data to index table. + * + * @param array $priceData * @param bool $useAdditionalTable * @return bool * @throws \Exception */ - public function execute(array $arrData, $useAdditionalTable = false) + public function execute(array $priceData, $useAdditionalTable = false) { - if (empty($arrData)) { + if (empty($priceData)) { return false; } @@ -61,13 +66,13 @@ public function execute(array $arrData, $useAdditionalTable = false) $productIds = []; try { - foreach ($arrData as $key => $data) { + foreach ($priceData as $key => $data) { $productIds['product_id'] = $data['product_id']; - $arrData[$key]['rule_date'] = $this->dateFormat->formatDate($data['rule_date'], false); - $arrData[$key]['latest_start_date'] = $this->dateFormat->formatDate($data['latest_start_date'], false); - $arrData[$key]['earliest_end_date'] = $this->dateFormat->formatDate($data['earliest_end_date'], false); + $priceData[$key]['rule_date'] = $this->dateFormat->formatDate($data['rule_date'], false); + $priceData[$key]['latest_start_date'] = $this->dateFormat->formatDate($data['latest_start_date'], false); + $priceData[$key]['earliest_end_date'] = $this->dateFormat->formatDate($data['earliest_end_date'], false); } - $connection->insertOnDuplicate($indexTable, $arrData); + $connection->insertOnDuplicate($indexTable, $priceData); } catch (\Exception $e) { throw $e; } diff --git a/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php index 41e620d5721ee..25d164aeee5c3 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php @@ -6,6 +6,9 @@ namespace Magento\CatalogRule\Model\Indexer; +/** + * Build select for rule relation with product. + */ class RuleProductsSelectBuilder { /** @@ -55,6 +58,8 @@ public function __construct( } /** + * Build select for indexer according passed parameters. + * * @param int $websiteId * @param \Magento\Catalog\Model\Product|null $product * @param bool $useAdditionalTable @@ -142,7 +147,7 @@ public function build( [] ); $select->columns([ - 'default_price' =>$connection->getIfNullSql($tableAlias . '.value', 'pp_default.value'), + 'default_price' => $connection->getIfNullSql($tableAlias . '.value', 'pp_default.value'), ]); return $connection->query($select); diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ProductPriceCalculatorTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ProductPriceCalculatorTest.php new file mode 100644 index 0000000000000..18b3fccfaa87c --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ProductPriceCalculatorTest.php @@ -0,0 +1,103 @@ +priceCurrencyMock = $this->getMockBuilder(\Magento\Framework\Pricing\PriceCurrencyInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->model = new \Magento\CatalogRule\Model\Indexer\ProductPriceCalculator($this->priceCurrencyMock); + } + + public function testCalculateToFixedPrice() + { + $rulePrice = 100; + $actionAmount = 50; + $ruleData = [ + 'action_operator' => 'to_fixed', + 'action_amount' => $actionAmount + ]; + $productData = ['rule_price' => $rulePrice]; + + $this->priceCurrencyMock->expects($this->once()) + ->method('round') + ->with($actionAmount) + ->willReturn($actionAmount); + + $this->assertEquals($actionAmount, $this->model->calculate($ruleData, $productData)); + } + + public function testCalculateToPercentPrice() + { + $rulePrice = 200; + $actionAmount = 50; + $expectedPrice = 100; + $ruleData = [ + 'action_operator' => 'to_percent', + 'action_amount' => $actionAmount + ]; + $productData = ['rule_price' => $rulePrice]; + + $this->priceCurrencyMock->expects($this->once()) + ->method('round') + ->with($expectedPrice) + ->willReturn($expectedPrice); + + $this->assertEquals($expectedPrice, $this->model->calculate($ruleData, $productData)); + } + + public function testCalculateByFixedPrice() + { + $rulePrice = 200; + $actionAmount = 50; + $expectedPrice = 150; + $ruleData = [ + 'action_operator' => 'by_fixed', + 'action_amount' => $actionAmount + ]; + $productData = ['rule_price' => $rulePrice]; + + $this->priceCurrencyMock->expects($this->once()) + ->method('round') + ->with($expectedPrice) + ->willReturn($expectedPrice); + + $this->assertEquals($expectedPrice, $this->model->calculate($ruleData, $productData)); + } + + public function testCalculateByPercentPrice() + { + $rulePrice = 200; + $actionAmount = 50; + $expectedPrice = 100; + $ruleData = [ + 'action_operator' => 'by_percent', + 'action_amount' => $actionAmount + ]; + $productData = ['rule_price' => $rulePrice]; + + $this->priceCurrencyMock->expects($this->once()) + ->method('round') + ->with($expectedPrice) + ->willReturn($expectedPrice); + + $this->assertEquals($expectedPrice, $this->model->calculate($ruleData, $productData)); + } +} diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleGroupWebsiteTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleGroupWebsiteTest.php new file mode 100644 index 0000000000000..a9f9d2b0c4b11 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleGroupWebsiteTest.php @@ -0,0 +1,108 @@ +dateTimeMock = $this->getMockBuilder(\Magento\Framework\Stdlib\DateTime\DateTime::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resourceMock = $this->getMockBuilder(\Magento\Framework\App\ResourceConnection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->activeTableSwitcherMock = + $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class) + ->disableOriginalConstructor() + ->getMock(); + $this->model = new \Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite( + $this->dateTimeMock, + $this->resourceMock, + $this->activeTableSwitcherMock + ); + } + + public function testExecute() + { + $timeStamp = (int)gmdate('U'); + $insertString = 'insert_string'; + $connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class)->getMock(); + $this->resourceMock->expects($this->at(0))->method('getConnection')->willReturn($connectionMock); + $this->dateTimeMock->expects($this->once())->method('gmtTimestamp')->willReturn($timeStamp); + + $this->activeTableSwitcherMock->expects($this->at(0)) + ->method('getAdditionalTableName') + ->with('catalogrule_group_website') + ->willReturn('catalogrule_group_website_replica'); + $this->activeTableSwitcherMock->expects($this->at(1)) + ->method('getAdditionalTableName') + ->with('catalogrule_product') + ->willReturn('catalogrule_product_replica'); + + $this->resourceMock->expects($this->at(1)) + ->method('getTableName') + ->with('catalogrule_group_website') + ->willReturn('catalogrule_group_website'); + $this->resourceMock->expects($this->at(2)) + ->method('getTableName') + ->with('catalogrule_product') + ->willReturn('catalogrule_product'); + $this->resourceMock->expects($this->at(3)) + ->method('getTableName') + ->with('catalogrule_group_website_replica') + ->willReturn('catalogrule_group_website_replica'); + $this->resourceMock->expects($this->at(4)) + ->method('getTableName') + ->with('catalogrule_product_replica') + ->willReturn('catalogrule_product_replica'); + + $selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + ->disableOriginalConstructor() + ->getMock(); + + $connectionMock->expects($this->once())->method('delete')->with('catalogrule_group_website_replica'); + $connectionMock->expects($this->once())->method('select')->willReturn($selectMock); + + $selectMock->expects($this->once())->method('distinct')->with(true)->willReturnSelf(); + $selectMock->expects($this->once()) + ->method('from') + ->with('catalogrule_product_replica', ['rule_id', 'customer_group_id', 'website_id']) + ->willReturnSelf(); + $selectMock->expects($this->once()) + ->method('where') + ->with("{$timeStamp} >= from_time AND (({$timeStamp} <= to_time AND to_time > 0) OR to_time = 0)") + ->willReturnSelf(); + $selectMock->expects($this->once()) + ->method('insertFromSelect') + ->with('catalogrule_group_website_replica', ['rule_id', 'customer_group_id', 'website_id']) + ->willReturn($insertString); + $connectionMock->expects($this->once())->method('query')->with($insertString); + + $this->assertTrue($this->model->execute(true)); + } +} diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductPriceTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductPriceTest.php new file mode 100644 index 0000000000000..99d66c20291dd --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductPriceTest.php @@ -0,0 +1,128 @@ +storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->ruleProductsSelectBuilderMock = + $this->getMockBuilder(\Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder::class) + ->disableOriginalConstructor() + ->getMock(); + $this->productPriceCalculatorMock = + $this->getMockBuilder(\Magento\CatalogRule\Model\Indexer\ProductPriceCalculator::class) + ->disableOriginalConstructor() + ->getMock(); + $this->dateTimeMock = $this->getMockBuilder(\Magento\Framework\Stdlib\DateTime\DateTime::class) + ->disableOriginalConstructor() + ->getMock(); + $this->pricesPersistorMock = + $this->getMockBuilder(\Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor::class) + ->disableOriginalConstructor() + ->getMock(); + $this->model = new \Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice( + $this->storeManagerMock, + $this->ruleProductsSelectBuilderMock, + $this->productPriceCalculatorMock, + $this->dateTimeMock, + $this->pricesPersistorMock + ); + } + + public function testExecute() + { + $websiteId = 234; + $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->getMock(); + + $websiteMock = $this->getMockBuilder(\Magento\Store\Api\Data\WebsiteInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $websiteMock->expects($this->once())->method('getId')->willReturn($websiteId); + $this->storeManagerMock->expects($this->once())->method('getWebsites')->willReturn([$websiteMock]); + + $statementMock = $this->getMockBuilder(\Zend_Db_Statement_Interface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->ruleProductsSelectBuilderMock->expects($this->once()) + ->method('build') + ->with($websiteId, $productMock, true) + ->willReturn($statementMock); + + $ruleData = [ + 'product_id' => 100, + 'website_id' => 1, + 'customer_group_id' => 2, + 'from_time' => mktime(0, 0, 0, date('m'), date('d') - 100), + 'to_time' => mktime(0, 0, 0, date('m'), date('d') + 100), + 'action_stop' => true + ]; + + $this->dateTimeMock->expects($this->at(0)) + ->method('date') + ->with('Y-m-d 00:00:00', $ruleData['from_time']) + ->willReturn($ruleData['from_time']); + $this->dateTimeMock->expects($this->at(1)) + ->method('timestamp') + ->with($ruleData['from_time']) + ->willReturn($ruleData['from_time']); + + $this->dateTimeMock->expects($this->at(2)) + ->method('date') + ->with('Y-m-d 00:00:00', $ruleData['to_time']) + ->willReturn($ruleData['to_time']); + $this->dateTimeMock->expects($this->at(3)) + ->method('timestamp') + ->with($ruleData['to_time']) + ->willReturn($ruleData['to_time']); + + $statementMock->expects($this->at(0))->method('fetch')->willReturn($ruleData); + $statementMock->expects($this->at(1))->method('fetch')->willReturn(false); + + $this->productPriceCalculatorMock->expects($this->atLeastOnce())->method('calculate'); + $this->pricesPersistorMock->expects($this->once())->method('execute'); + + $this->assertTrue($this->model->execute(1, $productMock, true)); + } +} diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductTest.php new file mode 100644 index 0000000000000..76c1e42b39ba1 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductTest.php @@ -0,0 +1,152 @@ +resourceMock = $this->getMockBuilder(\Magento\Framework\App\ResourceConnection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->activeTableSwitcherMock = + $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class) + ->disableOriginalConstructor() + ->getMock(); + $this->model = new \Magento\CatalogRule\Model\Indexer\ReindexRuleProduct( + $this->resourceMock, + $this->activeTableSwitcherMock + ); + } + + public function testExecuteIfRuleInactive() + { + $ruleMock = $this->getMockBuilder(\Magento\CatalogRule\Model\Rule::class) + ->disableOriginalConstructor() + ->getMock(); + $ruleMock->expects($this->once())->method('getIsActive')->willReturn(false); + $this->assertFalse($this->model->execute($ruleMock, 100, true)); + } + + public function testExecuteIfRuleWithoutWebsiteIds() + { + $ruleMock = $this->getMockBuilder(\Magento\CatalogRule\Model\Rule::class) + ->disableOriginalConstructor() + ->getMock(); + $ruleMock->expects($this->once())->method('getIsActive')->willReturn(true); + $ruleMock->expects($this->once())->method('getWebsiteIds')->willReturn(null); + $this->assertFalse($this->model->execute($ruleMock, 100, true)); + } + + public function testExecute() + { + $productIds = [ + 4 => [1 => 1], + 5 => [1 => 1], + 6 => [1 => 1], + ]; + $ruleMock = $this->getMockBuilder(\Magento\CatalogRule\Model\Rule::class) + ->disableOriginalConstructor() + ->getMock(); + $ruleMock->expects($this->once())->method('getIsActive')->willReturn(true); + $ruleMock->expects($this->exactly(2))->method('getWebsiteIds')->willReturn(1); + $ruleMock->expects($this->once())->method('getMatchingProductIds')->willReturn($productIds); + + $this->activeTableSwitcherMock->expects($this->once()) + ->method('getAdditionalTableName') + ->with('catalogrule_product') + ->willReturn('catalogrule_product_replica'); + + $connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resourceMock->expects($this->at(0))->method('getConnection')->willReturn($connectionMock); + $this->resourceMock->expects($this->at(1)) + ->method('getTableName') + ->with('catalogrule_product') + ->willReturn('catalogrule_product'); + $this->resourceMock->expects($this->at(2)) + ->method('getTableName') + ->with('catalogrule_product_replica') + ->willReturn('catalogrule_product_replica'); + + $ruleMock->expects($this->once())->method('getId')->willReturn(100); + $ruleMock->expects($this->once())->method('getCustomerGroupIds')->willReturn([10]); + $ruleMock->expects($this->once())->method('getFromDate')->willReturn('2017-06-21'); + $ruleMock->expects($this->once())->method('getToDate')->willReturn('2017-06-30'); + $ruleMock->expects($this->once())->method('getSortOrder')->willReturn(1); + $ruleMock->expects($this->once())->method('getSimpleAction')->willReturn('simple_action'); + $ruleMock->expects($this->once())->method('getDiscountAmount')->willReturn(43); + $ruleMock->expects($this->once())->method('getStopRulesProcessing')->willReturn(true); + + $batchRows = [ + [ + 'rule_id' => 100, + 'from_time' => 1498028400, + 'to_time' => 1498892399, + 'website_id' => 1, + 'customer_group_id' => 10, + 'product_id' => 4, + 'action_operator' => 'simple_action', + 'action_amount' => 43, + 'action_stop' => true, + 'sort_order' => 1, + ], + [ + 'rule_id' => 100, + 'from_time' => 1498028400, + 'to_time' => 1498892399, + 'website_id' => 1, + 'customer_group_id' => 10, + 'product_id' => 5, + 'action_operator' => 'simple_action', + 'action_amount' => 43, + 'action_stop' => true, + 'sort_order' => 1, + ] + ]; + + $rowsNotInBatch = [ + [ + 'rule_id' => 100, + 'from_time' => 1498028400, + 'to_time' => 1498892399, + 'website_id' => 1, + 'customer_group_id' => 10, + 'product_id' => 6, + 'action_operator' => 'simple_action', + 'action_amount' => 43, + 'action_stop' => true, + 'sort_order' => 1, + ] + ]; + + $connectionMock->expects($this->at(0)) + ->method('insertMultiple') + ->with('catalogrule_product_replica', $batchRows); + $connectionMock->expects($this->at(1)) + ->method('insertMultiple') + ->with('catalogrule_product_replica', $rowsNotInBatch); + + $this->assertTrue($this->model->execute($ruleMock, 2, true)); + } +} diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductPricesPersistorTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductPricesPersistorTest.php new file mode 100644 index 0000000000000..989d71bb086c4 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductPricesPersistorTest.php @@ -0,0 +1,163 @@ +dateTimeMock = $this->getMockBuilder(\Magento\Framework\Stdlib\DateTime::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resourceMock = $this->getMockBuilder(\Magento\Framework\App\ResourceConnection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->activeTableSwitcherMock = + $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class) + ->disableOriginalConstructor() + ->getMock(); + $this->model = new \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor( + $this->dateTimeMock, + $this->resourceMock, + $this->activeTableSwitcherMock + ); + } + + public function testExecuteWithEmptyPriceData() + { + $this->assertFalse($this->model->execute([])); + } + + public function testExecute() + { + $priceData = [ + [ + 'product_id' => 1, + 'rule_date' => '2017-05-01', + 'latest_start_date' => '2017-05-10', + 'earliest_end_date' => '2017-05-20', + ] + ]; + $tableName = 'catalogrule_product_price_replica'; + + $this->activeTableSwitcherMock->expects($this->once()) + ->method('getAdditionalTableName') + ->with('catalogrule_product_price') + ->willReturn($tableName); + + $connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resourceMock->expects($this->once())->method('getConnection')->willReturn($connectionMock); + $this->resourceMock->expects($this->at(1)) + ->method('getTableName') + ->with('catalogrule_product_price') + ->willReturn('catalogrule_product_price'); + $this->resourceMock->expects($this->at(2)) + ->method('getTableName') + ->with($tableName) + ->willReturn($tableName); + + $this->dateTimeMock->expects($this->at(0)) + ->method('formatDate') + ->with($priceData[0]['rule_date'], false) + ->willReturn($priceData[0]['rule_date']); + + $this->dateTimeMock->expects($this->at(1)) + ->method('formatDate') + ->with($priceData[0]['latest_start_date'], false) + ->willReturn($priceData[0]['latest_start_date']); + + $this->dateTimeMock->expects($this->at(2)) + ->method('formatDate') + ->with($priceData[0]['earliest_end_date'], false) + ->willReturn($priceData[0]['earliest_end_date']); + + $connectionMock->expects($this->once()) + ->method('insertOnDuplicate') + ->with($tableName, $priceData); + + $this->assertTrue($this->model->execute($priceData, true)); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Insert error. + */ + public function testExecuteWithException() + { + $priceData = [ + [ + 'product_id' => 1, + 'rule_date' => '2017-05-5', + 'latest_start_date' => '2017-05-10', + 'earliest_end_date' => '2017-05-22', + ] + ]; + $tableName = 'catalogrule_product_price_replica'; + + $this->activeTableSwitcherMock->expects($this->once()) + ->method('getAdditionalTableName') + ->with('catalogrule_product_price') + ->willReturn($tableName); + + $this->dateTimeMock->expects($this->at(0)) + ->method('formatDate') + ->with($priceData[0]['rule_date'], false) + ->willReturn($priceData[0]['rule_date']); + + $this->dateTimeMock->expects($this->at(1)) + ->method('formatDate') + ->with($priceData[0]['latest_start_date'], false) + ->willReturn($priceData[0]['latest_start_date']); + + $this->dateTimeMock->expects($this->at(2)) + ->method('formatDate') + ->with($priceData[0]['earliest_end_date'], false) + ->willReturn($priceData[0]['earliest_end_date']); + + $connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $connectionMock->expects($this->once()) + ->method('insertOnDuplicate') + ->with($tableName, $priceData) + ->willThrowException(new \Exception('Insert error.')); + + $this->resourceMock->expects($this->once())->method('getConnection')->willReturn($connectionMock); + $this->resourceMock->expects($this->at(1)) + ->method('getTableName') + ->with('catalogrule_product_price') + ->willReturn('catalogrule_product_price'); + $this->resourceMock->expects($this->at(2)) + ->method('getTableName') + ->with($tableName) + ->willReturn($tableName); + + $this->assertTrue($this->model->execute($priceData, true)); + } +} diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductsSelectBuilderTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductsSelectBuilderTest.php new file mode 100644 index 0000000000000..ee5ea7bd616e8 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductsSelectBuilderTest.php @@ -0,0 +1,187 @@ +storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) + ->getMockForAbstractClass(); + $this->resourceMock = $this->getMockBuilder(\Magento\Framework\App\ResourceConnection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->activeTableSwitcherMock = + $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class) + ->disableOriginalConstructor() + ->getMock(); + $this->eavConfigMock = $this->getMockBuilder(\Magento\Eav\Model\Config::class) + ->disableOriginalConstructor() + ->getMock(); + $this->metadataPoolMock = $this->getMockBuilder(\Magento\Framework\EntityManager\MetadataPool::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->model = new \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder( + $this->resourceMock, + $this->eavConfigMock, + $this->storeManagerMock, + $this->metadataPoolMock, + $this->activeTableSwitcherMock + ); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testBuild() + { + $websiteId = 55; + $ruleTable = 'catalogrule_product'; + $rplTable = 'catalogrule_product_replica'; + $prTable = 'catalog_product_entity'; + $wsTable = 'catalog_product_website'; + $productMock = $this->getMockBuilder(Product::class)->disableOriginalConstructor()->getMock(); + $productMock->expects($this->exactly(2))->method('getEntityId')->willReturn(500); + + $connectionMock = $this->getMockBuilder(AdapterInterface::class)->disableOriginalConstructor()->getMock(); + $this->resourceMock->expects($this->at(0))->method('getConnection')->willReturn($connectionMock); + + $this->activeTableSwitcherMock->expects($this->once()) + ->method('getAdditionalTableName') + ->with($ruleTable) + ->willReturn($rplTable); + + $this->resourceMock->expects($this->at(1))->method('getTableName')->with($ruleTable)->willReturn($ruleTable); + $this->resourceMock->expects($this->at(2))->method('getTableName')->with($rplTable)->willReturn($rplTable); + $this->resourceMock->expects($this->at(3))->method('getTableName')->with($prTable)->willReturn($prTable); + $this->resourceMock->expects($this->at(4))->method('getTableName')->with($wsTable)->willReturn($wsTable); + + $selectMock = $this->getMockBuilder(Select::class)->disableOriginalConstructor()->getMock(); + $connectionMock->expects($this->once())->method('select')->willReturn($selectMock); + $selectMock->expects($this->at(0))->method('from')->with(['rp' => $rplTable])->willReturnSelf(); + $selectMock->expects($this->at(1)) + ->method('order') + ->with(['rp.website_id', 'rp.customer_group_id', 'rp.product_id', 'rp.sort_order', 'rp.rule_id']) + ->willReturnSelf(); + $selectMock->expects($this->at(2))->method('where')->with('rp.product_id=?', 500)->willReturnSelf(); + + $attributeMock = $this->getMockBuilder(AbstractAttribute::class)->disableOriginalConstructor()->getMock(); + $this->eavConfigMock->expects($this->once()) + ->method('getAttribute') + ->with(Product::ENTITY, 'price') + ->willReturn($attributeMock); + $backendMock = $this->getMockBuilder(AbstractBackend::class)->disableOriginalConstructor()->getMock(); + $backendMock->expects($this->once())->method('getTable')->willReturn('price_table'); + $attributeMock->expects($this->once())->method('getBackend')->willReturn($backendMock); + $attributeMock->expects($this->once())->method('getId')->willReturn(200); + + $metadataMock = $this->getMockBuilder(EntityMetadataInterface::class)->disableOriginalConstructor()->getMock(); + $this->metadataPoolMock->expects($this->once()) + ->method('getMetadata') + ->with(\Magento\Catalog\Api\Data\ProductInterface::class) + ->willReturn($metadataMock); + $metadataMock->expects($this->once())->method('getLinkField')->willReturn('link_field'); + + $selectMock->expects($this->at(3)) + ->method('join') + ->with(['e' => $prTable], 'e.entity_id = rp.product_id', []) + ->willReturnSelf(); + $selectMock->expects($this->at(4)) + ->method('join') + ->with( + ['pp_default' => 'price_table'], + 'pp_default.link_field=e.link_field AND (pp_default.attribute_id=200) and pp_default.store_id=0', + [] + )->willReturnSelf(); + $websiteMock = $this->getMockBuilder(WebsiteInterface::class) + ->setMethods(['getDefaultGroup']) + ->getMockForAbstractClass(); + $this->storeManagerMock->expects($this->once()) + ->method('getWebsite') + ->with($websiteId) + ->willReturn($websiteMock); + + $groupMock = $this->getMockBuilder(\Magento\Store\Model\Group::class) + ->setMethods(['getDefaultStoreId']) + ->disableOriginalConstructor() + ->getMock(); + $websiteMock->expects($this->once())->method('getDefaultGroup')->willReturn($groupMock); + $groupMock->expects($this->once())->method('getDefaultStoreId')->willReturn(700); + + $selectMock->expects($this->at(5)) + ->method('joinInner') + ->with( + ['product_website' => $wsTable], + 'product_website.product_id=rp.product_id ' + . 'AND product_website.website_id = rp.website_id ' + . 'AND product_website.website_id=' + . $websiteId, + [] + )->willReturnSelf(); + $selectMock->expects($this->at(6)) + ->method('joinLeft') + ->with( + ['pp' . $websiteId => 'price_table'], + 'pp55.link_field=e.link_field AND (pp55.attribute_id=200) and pp55.store_id=700', + [] + )->willReturnSelf(); + + $connectionMock->expects($this->once()) + ->method('getIfNullSql') + ->with('pp55.value', 'pp_default.value') + ->willReturn('IF NULL SQL'); + $selectMock->expects($this->at(7)) + ->method('columns') + ->with(['default_price' => 'IF NULL SQL']) + ->willReturnSelf(); + $statementMock = $this->getMockBuilder(\Zend_Db_Statement_Interface::class) + ->disableOriginalConstructor() + ->getMock(); + $connectionMock->expects($this->once())->method('query')->with($selectMock)->willReturn($statementMock); + + $this->assertEquals($statementMock, $this->model->build($websiteId, $productMock, true)); + } +} diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/RuleTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/RuleTest.php index 2e46d532d59d0..2f790db15aebe 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/RuleTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/RuleTest.php @@ -411,4 +411,10 @@ public function testGetConditionsFieldSetId() $expectedResult = 'form_namerule_conditions_fieldset_100'; $this->assertEquals($expectedResult, $this->rule->getConditionsFieldSetId($formName)); } + + public function testReindex() + { + $this->_ruleProductProcessor->expects($this->once())->method('reindexList'); + $this->rule->reindex(); + } } From 5b9e1c96d0700f159134752d07807c90e03663cb Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Fri, 23 Jun 2017 14:17:06 +0300 Subject: [PATCH 36/39] MAGETWO-65422: Write default configs to shared configuration file by app:config:dump --- .../Unit/Model/PreparedValueFactoryTest.php | 1 - .../Unit/Model/ScopeTypeNormalizerTest.php | 26 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Config/Test/Unit/Model/PreparedValueFactoryTest.php b/app/code/Magento/Config/Test/Unit/Model/PreparedValueFactoryTest.php index 2dfff40024106..e883f17dfbe4d 100644 --- a/app/code/Magento/Config/Test/Unit/Model/PreparedValueFactoryTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/PreparedValueFactoryTest.php @@ -99,7 +99,6 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); $this->fieldMock = $this->getMockBuilder(Field::class) - //->setMethods(['getData', 'getConfigPath', 'getId', 'hasBackendModel']) ->disableOriginalConstructor() ->getMock(); $this->valueMock = $this->getMockBuilder(Value::class) diff --git a/app/code/Magento/Store/Test/Unit/Model/ScopeTypeNormalizerTest.php b/app/code/Magento/Store/Test/Unit/Model/ScopeTypeNormalizerTest.php index 6d54c43f2ee29..6ff6e34a08100 100644 --- a/app/code/Magento/Store/Test/Unit/Model/ScopeTypeNormalizerTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/ScopeTypeNormalizerTest.php @@ -56,4 +56,30 @@ public function normalizeDataProvider() ['default', false, 'default'], ]; } + + /** + * @param string $scopeType + * @param string $expectedResult + * @dataProvider normalizeDefaultDataProvider + */ + public function testNormalizeDefault($scopeType, $expectedResult) + { + $this->assertEquals($expectedResult, $this->scopeTypeNormalizer->normalize($scopeType)); + } + + /** + * @return array + */ + public function normalizeDefaultDataProvider() + { + return [ + [ScopeInterface::SCOPE_WEBSITE, ScopeInterface::SCOPE_WEBSITES], + [ScopeInterface::SCOPE_WEBSITES, ScopeInterface::SCOPE_WEBSITES], + [ScopeInterface::SCOPE_GROUP, ScopeInterface::SCOPE_GROUPS], + [ScopeInterface::SCOPE_GROUPS, ScopeInterface::SCOPE_GROUPS], + [ScopeInterface::SCOPE_STORE, ScopeInterface::SCOPE_STORES], + [ScopeInterface::SCOPE_STORES, ScopeInterface::SCOPE_STORES], + ['default', 'default'], + ]; + } } From aeed51aa3aeba4f4b6a0b7c9820dd370893b9b41 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov Date: Mon, 26 Jun 2017 08:55:25 +0300 Subject: [PATCH 37/39] MAGETWO-64518: \Magento\CatalogRule\Model\Indexer\IndexBuilder method "doReindexFull()" causes temporary missing sale prices --- .../Model/Indexer/RuleProductPricesPersistor.php | 10 ++++++++-- .../Model/Indexer/RuleProductsSelectBuilderTest.php | 3 +++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php index 8682849c37c85..853be1888b5b9 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php @@ -69,8 +69,14 @@ public function execute(array $priceData, $useAdditionalTable = false) foreach ($priceData as $key => $data) { $productIds['product_id'] = $data['product_id']; $priceData[$key]['rule_date'] = $this->dateFormat->formatDate($data['rule_date'], false); - $priceData[$key]['latest_start_date'] = $this->dateFormat->formatDate($data['latest_start_date'], false); - $priceData[$key]['earliest_end_date'] = $this->dateFormat->formatDate($data['earliest_end_date'], false); + $priceData[$key]['latest_start_date'] = $this->dateFormat->formatDate( + $data['latest_start_date'], + false + ); + $priceData[$key]['earliest_end_date'] = $this->dateFormat->formatDate( + $data['earliest_end_date'], + false + ); } $connection->insertOnDuplicate($indexTable, $priceData); } catch (\Exception $e) { diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductsSelectBuilderTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductsSelectBuilderTest.php index ee5ea7bd616e8..05f10633fba4c 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductsSelectBuilderTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductsSelectBuilderTest.php @@ -14,6 +14,9 @@ use Magento\Framework\EntityManager\EntityMetadataInterface; use Magento\Store\Api\Data\WebsiteInterface; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class RuleProductsSelectBuilderTest extends \PHPUnit_Framework_TestCase { /** From 0cc117fca5b0a67adcad897f0759934255d29716 Mon Sep 17 00:00:00 2001 From: Oleksandr Shmyheliuk Date: Thu, 29 Jun 2017 15:06:02 +0300 Subject: [PATCH 38/39] MAGETWO-69584: Command config:sensitive:set does not work on the cloud --- app/code/Magento/Config/Model/Config/Importer.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Config/Model/Config/Importer.php b/app/code/Magento/Config/Model/Config/Importer.php index ad5ea6d7c4912..bcf3ea0e5d4fe 100644 --- a/app/code/Magento/Config/Model/Config/Importer.php +++ b/app/code/Magento/Config/Model/Config/Importer.php @@ -122,14 +122,13 @@ public function import(array $data) $this->scopeConfig->clean(); } - $this->state->emulateAreaCode(Area::AREA_ADMINHTML, function () use ($changedData) { + $this->state->emulateAreaCode(Area::AREA_ADMINHTML, function () use ($changedData, $data) { $this->scope->setCurrentScope(Area::AREA_ADMINHTML); // Invoke saving of new values. $this->saveProcessor->process($changedData); + $this->flagManager->saveFlag(static::FLAG_CODE, $data); }); - - $this->flagManager->saveFlag(static::FLAG_CODE, $data); } catch (\Exception $e) { throw new InvalidTransitionException(__('%1', $e->getMessage()), $e); } finally { From 601a9a34cd78d82d554427adc3afa02f73f3a5da Mon Sep 17 00:00:00 2001 From: Ostap Smolyar Date: Thu, 29 Jun 2017 16:41:59 +0300 Subject: [PATCH 39/39] MQE-155: [FT] Magento\UrlRewrite\Test\TestCase\CreateProductWithSeveralWebsitesUrlRewriteTest randomly fails on Jenkins --- .../TestCase/CreateProductWithSeveralWebsitesUrlRewriteTest.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductWithSeveralWebsitesUrlRewriteTest.xml b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductWithSeveralWebsitesUrlRewriteTest.xml index a7243d01707e4..98e3f9e38382d 100644 --- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductWithSeveralWebsitesUrlRewriteTest.xml +++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductWithSeveralWebsitesUrlRewriteTest.xml @@ -8,8 +8,6 @@ - stable:no - MAGETWO-66532: [FT] Magento\UrlRewrite\Test\TestCase\CreateProductWithSeveralWebsitesUrlRewriteTest randomly fails on Jenkins simple-product-%isolation% Simple Product %isolation% simple_sku_%isolation%